软件安全不是安全软件。此处我们关注的主题包括身份验证、Access Control、机密性、加密和权限管理。
[Authorize]
属性授予的授权,同时在类或方法级别使用 [AllowAnonymous]
属性。[Authorize]
属性,使应用程序操作需要授权。此外,通过添加 [AllowAnonymous]
属性,可以绕过应用程序类或方法的指定授权要求。当同时指定两个属性时,[AllowAnonymous]
属性优先并绕过 [Authorize]
属性。这可能会导致攻击者对敏感数据和操作进行任意匿名访问。[AllowAnonymous]
属性覆盖在方法级别设置的 [Authorize]
属性的示例。尽管具有 [Authorize]
属性,secretAction()
方法也不需要授权。示例 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
值,会发生什么情况。尽管该界面会自动提交当前用户的雇员 ID,但是攻击者也可能在恶意请求中提交其他值替换当前的 ID。因为本例子中的代码是在匿名绑定情况下执行查询,不管当前已验证用户的身份如何,它都会将有效的雇员 ID 返回至目录入口。
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
值,会发生什么情况。因为本例子中的代码是在匿名绑定情况下执行查询,不管当前已验证用户的身份如何,它都会将有效的雇员 ID 返回至目录入口。
...
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
值,会发生什么情况。尽管该界面会自动提交当前用户的雇员 ID,但是攻击者也可能在恶意请求中提交其他值替换当前的 ID。因为本例子中的代码是在匿名绑定情况下执行查询,不管当前已验证用户的身份如何,它都会将有效的雇员 ID 返回至目录入口。
...
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.
...
AUTHORITY-CHECK
语句,同时附加 FOR USER
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>
元素表明程序未采用基于角色的访问控制,基于角色的访问控制是在安全 Web 应用程序中保护敏感操作的公认最佳实践。如果应用程序提供对敏感操作或数据的访问权限,则可能未部署充分的控制措施来防止未经授权的用户获取访问权限。此外,如果 <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>