軟體安全性並非安全性軟體。我們關注驗證、Access Control、保密性、加密以及權限管理之類的主題。
[AllowAnonymous]
屬性略過 [Authorize]
屬性授予的授權。[Authorize]
屬性新增到類別或方法,允許應用程式動作要求授權。此外,應用程式類別或方法的指定授權需求也可以透過新增 [AllowAnonymous]
屬性予以略過。當 [AllowAnonymous]
屬性和 [Authorize]
屬性兩者都指定時,會以前者為優先,並略過後者。這可能導致攻擊者對敏感資料和動作進行任意且匿名的存取。[AllowAnonymous]
屬性覆寫在方法上設定的 [Authorize]
屬性。secretAction()
方法不需要授權,儘管有 [Authorize]
屬性。範例 2:以下範例顯示超級類別之類別層級的
...
[AllowAnonymous]
public class Authorization_Test
{
[Authorize]
public IActionResult secretAction()
{
}
}
...
[AllowAnonymous]
屬性覆寫在子類別方法上設定的 [Authorize]
屬性。由於繼承的類別 Inherit_AA
具有 [AllowAnonymous]
屬性,因此儘管指定了 [Authorize]
屬性, secretAction()
方法也不需要授權。範例 3:下面顯示
...
[AllowAnonymous]
public abstract class Inherit_AA
{
}
public class Authorization_Test:Inherit_AA
{
[Authorize]
public IActionResult secretAction()
{
}
}
...
[AllowAnonymous]
屬性覆寫在方法層級上具有繼承的 [Authorize]
屬性。由於繼承的方法 secretAction()
具有 [AllowAnonymous]
屬性,因此儘管在覆寫方法上指定了 [Authorize]
屬性,secretAction()
方法也不需要授權。
...
public abstract class Inherit_AA
{
[AllowAnonymous]
public abstract IActionResult secretAction()
{
}
}
public class Authorization_Test:Inherit_AA
{
[Authorize]
public override IActionResult secretAction()
{
}
}
...
<apex:page controller="accessControl">
<apex:pageBlock >
<apex:pageBlockSection >
<apex:outputText value="Survey Name: "/>
<apex:inputText value="{!surveyName}"/>
</apex:pageBlockSection>
<apex:pageBlockSection >
<apex:outputText value="New Name: "/>
<apex:inputText value="{!newSurveyName}"/>
</apex:pageBlockSection>
<apex:pageBlockSection >
<apex:commandButton value="Update" action="{!updateName}"/>
</apex:pageBlockSection>
</apex:pageBlock>
</apex:page>
public String surveyName { get; set; }
public String newSurveyName { get; set; }
public PageReference updateName() {
Survey__c s = [SELECT Name FROM Survey__c WHERE Name=:surveyName];
s.Name = newSurveyName;
update s;
PageReference page = ApexPages.currentPage();
page.setRedirect(true);
return page;
}
String canned_acl = request.getParameter("acl");
CreateBucketRequest createBucketRequest = CreateBucketRequest.builder()
.bucket("foo")
.acl(canned_acl)
.createBucketConfiguration(CreateBucketConfiguration.builder().locationConstraint(region.id()).build())
.build();
acl
參數,但攻擊者仍可將此參數設為 public-read-write
,並授予對儲存貯體的完全匿名存取權。
...
String selectedInvoice = request.getParameter("invoiceDate");
...
AmazonSimpleDBClient sdbc = new AmazonSimpleDBClient(appAWSCredentials);
GetAttributesResult sdbResult = sdbc.getAttributes(new GetAttributesRequest("invoices", selectedInvoice));
...
Example 1
中的程式碼產生屬於目前使用者的清單,但攻擊者可能略過此行為來要求任何想要的清單。因為此範例中的程式碼沒有檢查以確保使用者擁有存取所要求清單的權限,所以即使清單不屬於目前使用者,程式碼也會顯示所有清單。
...
id = this.getIntent().getExtras().getInt("id");
cursor = db.query(Uri.parse(invoices), columns, "id = ? ", {id}, null, null, null);
...
id
值。雖然查詢產生了屬於目前使用者的識別碼清單,但攻擊者可能略過此行為而要求任何想要的清單。因為此範例中的程式碼並未確認使用者擁有存取所需清單的權限,所以程式碼會顯示所有清單,即使清單不屬於目前的使用者。DirectoryEntry de
。
...
de = new DirectoryEntry("LDAP://ad.example.com:389/ou=People,dc=example,dc=com");
...
de
的 LDAP 查詢會在未經 authentication 和 access control 的情況下執行。攻擊者可能會以意想不到的方式來操縱其中的一個查詢,以取得原本應受目錄的 access control 機制所保護的記錄。ldap_simple_bind_s()
匿名捆綁一個 LDAP 目錄。
...
rc = ldap_simple_bind_s( ld, NULL, NULL );
if ( rc != LDAP_SUCCESS ) {
...
}
...
ld
的 LDAP 查詢會在未經 authentication 和 access control 的情況下執行。攻擊者可能會以意想不到的方式來操縱其中的一個查詢,以取得原本應受目錄的 access control 機制所保護的記錄。DirContext ctx
。
...
env.put(Context.SECURITY_AUTHENTICATION, "none");
DirContext ctx = new InitialDirContext(env);
...
ctx
的 LDAP 查詢會在未經 authentication 和 access control 的情況下執行。攻擊者可能會以意想不到的方式來操縱其中的一個查詢,以取得原本應受目錄的 access control 機制所保護的記錄。
...
$ldapbind = ldap_bind ($ldap, $dn, $password = "" );
...
...
PARAMETERS: p_xfeld TYPE xfeld.
...
CALL FUNCTION 'BAPI_EMPLOYEE_GETDATA'
EXPORTING
employee_id = emp_id
authority_check = p_xfeld
IMPORTING
return = ret
TABLES
org_assignment = org_data
personal_data = pers_data
internal_control = con_data
communication = comm_data
archivelink = arlink.
...
p_xfeld
提供空白字元,則在傳回員工的個人和聯絡人資訊之前不會執行授權檢查。範例 2:以下程式碼會刪除指定的 Blob 容器及其內容。
...
var queueName = queryStringData['name'];
var queueSvc;
queueSvc = azureStorage.createQueueService();
...
queueSvc.deleteQueue(queueName, option, function(error, response){
if(!error){
// all the messages has been deleted
}
});
...
...
var containerName = queryStringData['name'];
var blobSvc;
blobSvc = azureStorage.createBlobService();
...
blobSvc.deleteContainer(containerName, function (error, response) {
if (!error) {
// all the content in the given container has been deleted
}
});
...
Example 1
和 Example 2
中的程式碼會刪除指定的佇列/Blob 容器及其屬於目前使用者/程式的訊息/內容,但攻擊者可以刪除該 Azure 帳戶的任何佇列/Blob。因為此範例中的程式碼不會進行檢查以確保使用者/程式具備清除所要求佇列/Blob 的權限,因此即使其不屬於目前的使用者/程式,程式碼也會清除佇列/Blob。
public boolean isTrusted(String paramString) {
if (this._applicationContext.getPackageName().equalsIgnoreCase(paramString)) {
return true;
}
DATA: id TYPE i.
...
id = request->get_form_field( 'invoiceID' ).
CONCATENATE `INVOICEID = '` id `'` INTO cl_where.
SELECT *
FROM invoices
INTO CORRESPONDING FIELDS OF TABLE itab_invoices
WHERE (cl_where).
ENDSELECT.
...
ID
值。雖然介面會產生屬於目前使用者的清單識別碼清單,但攻擊者可能略過此介面以要求任何想要的清單。因為此範例中的程式碼沒有檢查以確保使用者擁有存取所要求清單的權限,所以即使清單不屬於目前使用者,程式碼也會顯示所有清單。
...
var params:Object = LoaderInfo(this.root.loaderInfo).parameters;
var id:int = int(Number(params["invoiceID"]));
var query:String = "SELECT * FROM invoices WHERE id = :id";
stmt.sqlConnection = conn;
stmt.text = query;
stmt.parameters[":id"] = id;
stmt.execute();
...
id
值。雖然介面會產生屬於目前使用者的清單識別碼清單,但攻擊者可能略過此介面以要求任何想要的清單。因為此範例中的程式碼沒有檢查以確保使用者擁有存取所要求清單的權限,所以即使清單不屬於目前使用者,程式碼也會顯示所有清單。inputID
值來自於預先定義的清單,而繫結變數可協助防止 SOQL/SOSL injection。
...
result = [SELECT Name, Phone FROM Contact WHERE (IsDeleted = false AND Id=:inputID)];
...
inputID
的值。如果攻擊者能夠略過介面並使用其他值傳送要求,便可存取其他聯絡人資訊。因為此範例中的程式碼沒有檢查以確保使用者擁有存取所要求聯絡人的權限,所以即使該使用者未獲得查看的授權,程式碼也會顯示任何聯絡人。
...
int16 id = System.Convert.ToInt16(invoiceID.Text);
var invoice = OrderSystem.getInvoices()
.Where(new Invoice { invoiceID = id });
...
id
值。雖然介面會產生屬於目前使用者的清單識別碼清單,但攻擊者可能略過此介面以要求任何想要的清單。因為此範例中的程式碼沒有檢查以確保使用者擁有存取所要求清單的權限,所以即使清單不屬於目前使用者,程式碼也會顯示所有清單。
...
CMyRecordset rs(&dbms);
rs.PrepareSQL("SELECT * FROM invoices WHERE id = ?");
rs.SetParam_int(0,atoi(r.Lookup("invoiceID").c_str()));
rs.SafeExecuteSQL();
...
id
值。雖然介面會產生屬於目前使用者的清單識別碼清單,但攻擊者可能略過此介面以要求任何想要的清單。因為此範例中的程式碼沒有檢查以確保使用者擁有存取所要求清單的權限,所以即使清單不屬於目前使用者,程式碼也會顯示所有清單。
...
ACCEPT ID.
EXEC SQL
DECLARE C1 CURSOR FOR
SELECT INVNO, INVDATE, INVTOTAL
FROM INVOICES
WHERE INVOICEID = :ID
END-EXEC.
...
ID
值。雖然介面會產生屬於目前使用者的清單識別碼清單,但攻擊者可能略過此介面以要求任何想要的清單。因為此範例中的程式碼沒有檢查以確保使用者擁有存取所要求清單的權限,所以即使清單不屬於目前使用者,程式碼也會顯示所有清單。deleteDatabase
方法可能會允許攻擊者刪除任何資料庫。
...
id := request.FormValue("invoiceID")
query := "SELECT * FROM invoices WHERE id = ?";
rows, err := db.Query(query, id)
...
id
值。雖然介面會產生屬於目前使用者的清單識別碼清單,但攻擊者可能略過此介面以要求任何想要的清單。因為此範例中的程式碼沒有檢查以確保使用者擁有存取所要求清單的權限,所以即使清單不屬於目前使用者,程式碼也會顯示所有清單。
...
id = Integer.decode(request.getParameter("invoiceID"));
String query = "SELECT * FROM invoices WHERE id = ?";
PreparedStatement stmt = conn.prepareStatement(query);
stmt.setInt(1, id);
ResultSet results = stmt.execute();
...
id
值。雖然介面會產生屬於目前使用者的清單識別碼清單,但攻擊者可能略過此介面以要求任何想要的清單。因為此範例中的程式碼沒有檢查以確保使用者擁有存取所要求清單的權限,所以即使清單不屬於目前使用者,程式碼也會顯示所有清單。Example 1
以適用於 Android 平台。
...
String id = this.getIntent().getExtras().getString("invoiceID");
String query = "SELECT * FROM invoices WHERE id = ?";
SQLiteDatabase db = this.openOrCreateDatabase("DB", MODE_PRIVATE, null);
Cursor c = db.rawQuery(query, new Object[]{id});
...
...
var id = document.form.invoiceID.value;
var query = "SELECT * FROM invoices WHERE id = ?";
db.transaction(function (tx) {
tx.executeSql(query,[id]);
}
)
...
id
值。雖然介面會產生屬於目前使用者的清單識別碼清單,但攻擊者可能略過此介面以要求任何想要的清單。因為此範例中的程式碼沒有檢查以確保使用者擁有存取所要求清單的權限,所以即使清單不屬於目前使用者,程式碼也會顯示所有清單。
...
NSManagedObjectContext *context = [appDelegate managedObjectContext];
NSEntityDescription *entityDesc = [NSEntityDescription entityForName:@"Invoices" inManagedObjectContext:context];
NSFetchRequest *request = [[NSFetchRequest alloc] init];
[request setEntity:entityDesc];
NSPredicate *pred = [NSPredicate predicateWithFormat:@"(id = %@)", invoiceId.text];
[request setPredicate:pred];
NSManagedObject *matches = nil;
NSError *error;
NSArray *objects = [context executeFetchRequest:request error:&error];
if ([objects count] == 0) {
status.text = @"No records found.";
} else {
matches = [objects objectAtIndex:0];
invoiceReferenceNumber.text = [matches valueForKey:@"invRefNum"];
orderNumber.text = [matches valueForKey:@"orderNumber"];
status.text = [NSString stringWithFormat:@"%d records found", [objects count]];
}
[request release];
...
id
值。雖然介面會產生屬於目前使用者的清單識別碼清單,但攻擊者可能略過此介面以要求任何想要的清單。因為此範例中的程式碼沒有檢查以確保使用者擁有存取所要求清單的權限,所以即使清單不屬於目前使用者,程式碼也會顯示所有清單。
...
$id = $_POST['id'];
$query = "SELECT * FROM invoices WHERE id = ?";
$stmt = $mysqli->prepare($query);
$stmt->bind_param('ss',$id);
$stmt->execute();
...
id
值。雖然介面會產生屬於目前使用者的清單識別碼清單,但攻擊者可能略過此介面以要求任何想要的清單。因為此範例中的程式碼沒有檢查以確保使用者擁有存取所要求清單的權限,所以即使清單不屬於目前使用者,程式碼也會顯示所有清單。
procedure get_item (
itm_cv IN OUT ItmCurTyp,
id in varchar2)
is
open itm_cv for ' SELECT * FROM items WHERE ' ||
'invoiceID = :invid' ||
using id;
end get_item;
id
值。雖然介面會產生屬於目前使用者的清單識別碼清單,但攻擊者可能略過此介面以要求任何想要的清單。因為此範例中的程式碼沒有檢查以確保使用者擁有存取所要求清單的權限,所以即使清單不屬於目前使用者,程式碼也會顯示所有清單。
...
id = request.POST['id']
c = db.cursor()
stmt = c.execute("SELECT * FROM invoices WHERE id = %s", (id,))
...
id
值。雖然介面會產生屬於目前使用者的清單識別碼清單,但攻擊者可能略過此介面以要求任何想要的清單。因為此範例中的程式碼沒有檢查以確保使用者擁有存取所要求清單的權限,所以即使清單不屬於目前使用者,程式碼也會顯示所有清單。
...
id = req['invoiceID'].respond_to(:to_int)
query = "SELECT * FROM invoices WHERE id=?"
stmt = conn.prepare(query)
stmt.execute(id)
...
id
值。雖然介面會產生屬於目前使用者的清單識別碼清單,但攻擊者可能略過此介面以要求任何想要的清單。因為此範例中的程式碼沒有檢查以確保使用者擁有存取所要求清單的權限,所以即使清單不屬於目前使用者,程式碼也會顯示所有清單。
def searchInvoice(value:String) = Action.async { implicit request =>
val result: Future[Seq[Invoice]] = db.run {
sql"select * from invoices where id=$value".as[Invoice]
}
...
}
id
值。雖然介面會產生屬於目前使用者的清單識別碼清單,但攻擊者可能略過此介面以要求任何想要的清單。因為此範例中的程式碼沒有檢查以確保使用者擁有存取所要求清單的權限,所以即使清單不屬於目前使用者,程式碼也會顯示所有清單。
...
let fetchRequest = NSFetchRequest()
let entity = NSEntityDescription.entityForName("Invoices", inManagedObjectContext: managedContext)
fetchRequest.entity = entity
let pred : NSPredicate = NSPredicate(format:"(id = %@)", invoiceId.text)
fetchRequest.setPredicate = pred
do {
let results = try managedContext.executeFetchRequest(fetchRequest)
let result : NSManagedObject = results.first!
invoiceReferenceNumber.text = result.valueForKey("invRefNum")
orderNumber.text = result.valueForKey("orderNumber")
status.text = "\(results.count) records found"
} catch let error as NSError {
print("Error \(error)")
}
...
id
值。雖然介面會產生屬於目前使用者的清單識別碼清單,但攻擊者可能略過此介面以要求任何想要的清單。因為此範例中的程式碼沒有檢查以確保使用者擁有存取所要求清單的權限,所以即使清單不屬於目前使用者,程式碼也會顯示所有清單。
...
id = Request.Form("invoiceID")
strSQL = "SELECT * FROM invoices WHERE id = ?"
objADOCommand.CommandText = strSQL
objADOCommand.CommandType = adCmdText
set objADOParameter = objADOCommand.CreateParameter("id" , adString, adParamInput, 0, 0)
objADOCommand.Parameters("id") = id
...
id
值。雖然介面會產生屬於目前使用者的清單識別碼清單,但攻擊者可能略過此介面以要求任何想要的清單。因為此範例中的程式碼沒有檢查以確保使用者擁有存取所要求清單的權限,所以即使清單不屬於目前使用者,程式碼也會顯示所有清單。
...
ACCEPT ID.
EXEC DLI
GU
SEGMENT(INVOICES)
WHERE (INVOICEID = ID)
END-EXEC.
...
ID
值。雖然介面會產生屬於目前使用者的清單識別碼清單,但攻擊者可能略過此介面以要求任何想要的清單。因為此範例中的程式碼沒有檢查以確保使用者擁有存取所要求清單的權限,所以即使清單不屬於目前使用者,程式碼也會顯示所有清單。aspnet:UseLegacyFormsAuthenticationTicketCompatibility
設定進行修改,以允許未經驗證的輸入傳遞至原生 API,且可能導致攻擊者能夠避開驗證。成功攻擊此弱點的攻擊者無需密碼,即能夠避開針對任何已知使用者名稱的 ASP.NET 表單驗證。然後,攻擊者能夠以受害使用者的名義採取任何動作,包括在網站上執行任意指令。aspnet:UseLegacyFormsAuthenticationTicketCompatibility
設定為 true
。
...
<appSettings>
<add key="aspnet:UseLegacyFormsAuthenticationTicketCompatibility" value="true" />
</appSettings>
...
static string AllowlistVerify(string name) {
Regex pattern = new Regex(@"^[a-zA-Z\-\.']+$");
if (pattern.IsMatch(name)) {
return name;
}
return null;
}
...
string verifiedName = AllowlistVerify(managerName.Text.trim());
if(verifiedName != null) {
DirectorySearcher src = new DirectorySearcher("(manager=" + verifiedName + ")");
src.SearchRoot = de;
src.SearchScope = SearchScope.Subtree;
foreach(SearchResult res in src.FindAll()) {
...
}
}
empName
的值,會發生什麼情況。儘管介面自動傳遞目前使用者的員工識別碼,但是攻擊者仍可能在惡意要求傳遞其他代替目前識別碼的值。因為此範例中的程式碼是在匿名連結下執行查詢,所以不管目前驗證的使用者身份如何,它都會將任何有效的員工識別碼傳回目錄項目中。
char* allowlist_verify(char* name) {
const char *error;
int errOffset;
char* regex = "^[a-zA-Z\\-\\.']+$";
pcre* re = pcre_compile(regex, 0, &err, &errOffset, NULL);
int rc = pcre_exec(re, NULL, name, strlen(name), 0, 0, NULL, 0);
if (rc == 1)
return name;
return NULL;
}
...
fgets(managerName, sizeof(managerName), socket);
char* verified_name = allowlist_verify(managerName);
if(verified_name != NULL) {
snprintf(filter, sizeof(filter), "(manager=%s)", verified_name);
if ( ( rc = ldap_search_ext_s( ld, FIND_DN, LDAP_SCOPE_BASE,
filter, NULL, 0, NULL, NULL, LDAP_NO_LIMIT,
LDAP_NO_LIMIT, &result ) ) == LDAP_SUCCESS ) {
...
}
}
username
的值,會發生什麼情況。因為此範例中的程式碼是在匿名連結下執行查詢,所以不管目前驗證的使用者身份如何,它都會將任何有效的員工識別碼傳回目錄項目中。
...
env.put(Context.SECURITY_AUTHENTICATION, "none");
DirContext ctx = new InitialDirContext(env);
String empID = request.getParameter("empID");
try
{
int id = Integer.parseInt(empID);
BasicAttribute attr = new BasicAttribute("empID", empID);
NamingEnumeration employee =
ctx.search("ou=People,dc=example,dc=com",attr);
...
empID
的值,會發生什麼情況。儘管介面自動傳遞目前使用者的員工識別碼,但是攻擊者仍可能在惡意要求傳遞其他代替目前識別碼的值。因為此範例中的程式碼是在匿名連結下執行查詢,所以不管目前驗證的使用者身份如何,它都會將任何有效的員工識別碼傳回目錄項目中。
...
CALL TRANSACTION 'SA38'.
...
MQOD-ALTERNATEUSERID
和 MQOD-ALTERNATESECURITYID
欄位。
...
10 MQOD.
** Alternate user identifier
15 MQOD-ALTERNATEUSERID PIC X(12).
** Alternate security identifier
15 MQOD-ALTERNATESECURITYID PIC X(40).
...
...
ACCEPT MQOD-ALTERNATEUSERID.
ACCEPT MQOD-ALTERNATESECURITYID.
CALL 'MQOPEN' USING HCONN, MQOD, OPTS, HOBJ, COMPOCODE REASON.
...
FOR USER
的同時使用陳述式 AUTHORITY-CHECK
AUTHORITY_CHECK
SU_RAUTH_CHECK_FOR_USER
...
AUTHORITY-CHECK OBJECT 'S_TCODE' FOR USER v_user
ID 'TCD' FIELD 'SA38'.
IF sy-subrc = 0.
CALL TRANSACTION 'SA38'.
ELSE.
...
with sharing
:將強制執行使用者共用規則。without sharing
:將不會被強制執行共用規則。inherited sharing
:將強制執行呼叫類別的共用規則。當呼叫類別未指定共用關鍵字時,或者類別本身是進入點 (例如 Visualforce 控制器) 時,預設將強制執行使用者共用規則。
public class TestClass1 {
public List<Contact> getAllTheSecrets() {
return Database.query('SELECT Name FROM Contact');
}
}
public without sharing class TestClass2 {
public List<Contact> getAllTheSecrets() {
return Database.query('SELECT Name FROM Contact');
}
}
public inherited sharing class TestClass3 {
public List<Contact> getAllTheSecrets() {
return Database.query('SELECT Name FROM Contact');
}
}
TestClass1
和 TestClass2
都不安全,因為可以在不強制執行使用者共用規則的情況下擷取記錄。TestClass3
較為安全,因為它預設會強制執行共用規則。但是,如果在一個明確宣告 without sharing
的類別中呼叫 getAllTheSecrets()
函數,則仍會授予未經授權的存取。<security constraint>
元素表示程式並未使用以角色為基礎的存取控制,這對保護網路應用程式的敏感作業而言,是一般公認的最佳作法。若應用程式提供敏感作業或資料的存取權限,可能沒有足夠的控制機制來防止未授權的使用者取得存取權限。此外,若 <url-pattern>
中有萬用字元 (*),這可能表示模式範圍過大。
<property name="filterInvocationDefinitionSource">
<value>
CONVERT_URL_TO_LOWERCASE_BEFORE_COMPARISON
\A/secure/.*\Z=REQUIRES_SECURE_CHANNEL
\A/acegilogin.jsp.*\Z=REQUIRES_SECURE_CHANNEL
\A/j_acegi_security_check.*\Z=REQUIRES_SECURE_CHANNEL
\A.*\Z=REQUIRES_INSECURE_CHANNEL
</value>
</property>