程式碼品質不佳,會導致無法預料的行為。從使用者的角度來看,這通常表現為可用性不佳。對於攻擊者而言,這提供了以意想不到的方式向系統施加壓力的機會。
<script>
標籤。
...
public String tagProcessor(String tag){
if (tag.toUpperCase().equals("SCRIPT")){
return null;
}
//does not contain SCRIPT tag, keep processing input
...
}
...
Example 1
的問題是,在無地區設定的情況使用 java.lang.String.toUpperCase()
時,它會使用預設地區設定的規則。使用土耳其文地區設定 "title".toUpperCase()
會傳回「T\u0130TLE」,其中「\u0130」是「LATIN CAPITAL LETTER I WITH DOT ABOVE」字元。這會導致非預期的結果,例如,在 Example 1
中,這會阻止此驗證攔截「script」一字,進而可能導致 Cross-Site Scripting 弱點。
...
import java.sql.PreparedStatement;
import com.sap.sql.NativeSQLAccess;
String mssOnlyStmt = "...";
// variant 1
PreparedStatement ps =
NativeSQLAccess.prepareNativeStatement(
conn, mssOnlyStmt);
. . .
// variant 2
Statement stmt =
NativeSQLAccess.createNativeStatement(conn);
int result = stmt.execute(mssOnlyStmt);
. . .
// variant 3
CallableStatement cs =
NativeSQLAccess.prepareNativeCall(
conn, mssOnlyStmt);
. . .
...
public class Box{
public int area;
public static final int width = 10;
public static final Box box = new Box();
public static final int height = (int) (Math.random() * 100);
public Box(){
area = width * height;
}
...
}
...
Example 1
中,開發人員預期 box.area
會是一個隨機整數,這正好是 10 的倍數,因為 width
等於 10。然而實際上,它的硬式編碼值始終是 0。使用編譯時間常數宣告的靜態 final 欄位會先進行初始化,然後依序執行每個欄位。這表示,由於 height
不是編譯時間常數,所以會在宣告 box
之後宣告,因此,會在初始化 height
欄位之前呼叫建構函式。
...
class Foo{
public static final int f = Bar.b - 1;
...
}
...
class Bar{
public static final int b = Foo.f + 1;
...
}
This example is perhaps easier to identify, but would be dependent on which class is loaded first by the JVM. In this exampleFoo.f
could be either -1 or 0, andBar.b
could be either 0 or 1.
null
之前先解除參照可能為 null
的指標,將會造成「解除參照之後檢查」的錯誤。當程式執行明確的 null
檢查,但仍繼續解除參照已知為 null
的指標時,將會造成「檢查後解除參照」的錯誤。這種類型的錯誤通常是打字錯誤或程式設計師的疏忽所造成。當程式明確的將某指標設定為 null
,卻在之後解除參考該指標,則會發生儲存後解除參照錯誤。這種錯誤通常是由於程式設計師在宣告變數時將變數初始化為 null
所致。foo
是 null
,並在之後以錯誤的方式解除參照。在 if
陳述式中檢查 foo
時,若其為 null
,便會發生 null
解除參照,因此造成 Null 指標異常。範例 2:在以下程式碼中,程式設計師會假設
if (foo is null) {
foo.SetBar(val);
...
}
foo
變數不是 null
,並解除參照物件來確認這項假設。不過,程式設計師之後將對照 null
檢查 foo
,來反駁這項假設。在 if
陳述式中檢查 foo
時,若其為 null
,則在解除參照時也可為 null
,並有可能會造成 Null 指標異常。解除參照不安全,後續檢查也不必要。範例 3:在以下程式碼中,程式設計師明確地將變數
foo.SetBar(val);
...
if (foo is not null) {
...
}
foo
設定為 null
。之後,程式設計師先解除參照 foo
,再檢查物件是否為 null
值。
Foo foo = null;
...
foo.SetBar(val);
...
}
null
之前先解除參照可能為 null
的指標,將會造成「解除參照之後檢查」的錯誤。當程式執行明確的 null
檢查,但仍繼續解除參照已知為 null
的指標時,將會造成「檢查後解除參照」的錯誤。這種類型的錯誤通常是打字錯誤或程式設計師的疏忽所造成。當程式明確的將某指標設定為 null
,卻在之後解除參考該指標,則會發生儲存後解除參照錯誤。這種錯誤通常是由於程式設計師在宣告變數時將變數初始化為 null
所致。ptr
不是 NULL
。當程式設計師取消參照指標時,這個假設就變得清楚。當程式設計師檢查 ptr
為 NULL
時,這個假設其實就出現其矛盾之處。若在 if
指令中檢查 ptr
時,其可為 NULL
,則解除參照時也可為 NULL
,並產生分段錯誤。範例 2:在以下程式碼中,程式設計師確定變數
ptr->field = val;
...
if (ptr != NULL) {
...
}
ptr
是 NULL
,並在之後以錯誤的方式解除參照。若在 if
陳述式中檢查 ptr
時,其非 NULL
,就會發生 null
解除參照,從而導致分段錯誤。範例 3:在以下程式碼中,程式設計師忘記了字串
if (ptr == null) {
ptr->field = val;
...
}
'\0'
實際上是 0 還是 NULL
,因此解除參照 Null 指標,並導致分段錯誤。範例 4:在以下程式碼中,程式設計師明確地將變數
if (ptr == '\0') {
*ptr = val;
...
}
ptr
設定為 NULL
。之後,程式設計師先解除參照 ptr
,再檢查物件是否為 null
值。
*ptr = NULL;
...
ptr->field = val;
...
}
null
檢查,但仍繼續解除參照已知為 null
的物件時,將會造成「解除參照之後檢查」的錯誤。這種類型的錯誤通常是打字錯誤或程式設計師的疏忽所造成。foo
是 null
,並在之後以錯誤的方式解除參照。在 if
陳述式中檢查 foo
時,若其為 null
,便會發生 null
解除參照,因此造成 Null 指標異常。
if (foo == null) {
foo.setBar(val);
...
}
require
的呼叫。
function withdrawWinnings() {
require(uint32(msg.sender) == 0);
_sendWinnings();
}
function _sendWinnings() {
msg.sender.transfer(this.balance);
}
assert
來檢查 Lock
合約執行個體的餘額是否為特定值 (msg.value
)。
contract Lock {
constructor (address owner, uint256 unlockTime) public payable {
...
}
}
contract Lockdrop {
...
function lock(...) {
uint256 eth = msg.value;
address owner = msg.sender;
uint256 unlockTime = unlockTimeForTerm(term);
Lock lockAddr = (new Lock).value(eth)(owner, unlockTime);
assert(address(lockAddr).balance == msg.value);
}
}
transfer()
和 send()
,其使用 2300 的固定燃料量。
interface ICallable {
function callMe() external;
}
contract HardcodedNotGood {
function callWithArgs() public {
callable.callMe{gas: 10000}();
}
}