...
var fs:FileStream = new FileStream();
fs.open(new File("config.properties"), FileMode.READ);
var decoder:Base64Decoder = new Base64Decoder();
decoder.decode(fs.readMultiByte(fs.bytesAvailable, File.systemCharset));
var password:String = decoder.toByteArray().toString();
URLRequestDefaults.setLoginCredentialsForHost(hostname, usr, password);
...
config.properties
에 액세스할 수 있는 모든 사용자는 password
값을 읽고 base64로 인코딩된 값임을 쉽게 알 수 있습니다. 비양심적인 직원이 이 정보에 대한 액세스 권한을 갖게 되면 이를 사용하여 시스템에 침입할 수 있습니다.
...
string value = regKey.GetValue(passKey).ToString());
byte[] decVal = Convert.FromBase64String(value);
NetworkCredential netCred =
new NetworkCredential(username,decVal.toString(),domain);
...
password
의 값을 읽을 수 있습니다. 비양심적인 직원이 이 정보에 대한 액세스 권한을 갖게 되면 이를 사용하여 시스템에 침입할 수 있습니다.
...
RegQueryValueEx(hkey, TEXT(.SQLPWD.), NULL,
NULL, (LPBYTE)password64, &size64);
Base64Decode(password64, size64, (BYTE*)password, &size);
rc = SQLConnect(*hdbc, server, SQL_NTS, uid,
SQL_NTS, password, SQL_NTS);
...
password64
값을 읽고 값이 base64로 인코딩되었다는 것을 쉽게 알 수 있습니다. 비양심적인 직원이 이 정보에 대한 접근 권한을 갖게 되면 이를 사용하여 시스템에 침입할 수 있습니다.
...
01 RECORDX.
05 UID PIC X(10).
05 PASSWORD PIC X(10).
05 LEN PIC S9(4) COMP.
...
EXEC CICS
READ
FILE('CFG')
INTO(RECORDX)
RIDFLD(ACCTNO)
...
END-EXEC.
CALL "g_base64_decode_inplace" using
BY REFERENCE PASSWORD
BY REFERENCE LEN
ON EXCEPTION
DISPLAY "Requires GLib library" END-DISPLAY
END-CALL.
EXEC SQL
CONNECT :UID
IDENTIFIED BY :PASSWORD
END-EXEC.
...
CFG
에 액세스할 수 있는 모든 사용자는 암호 값을 읽고 base64로 인코딩된 값임을 쉽게 알 수 있습니다. 비양심적인 직원이 이 정보에 대한 액세스 권한을 갖게 되면 이를 사용하여 시스템에 침입할 수 있습니다.
...
file, _ := os.Open("config.json")
decoder := json.NewDecoder(file)
decoder.Decode(&values)
password := base64.StdEncoding.DecodeString(values.Password)
request.SetBasicAuth(values.Username, password)
...
config.json
에 액세스할 수 있는 모든 사용자는 password
의 값을 읽고 base64로 인코딩된 값임을 쉽게 알 수 있습니다. 비양심적인 직원이 이 정보에 대한 접근 권한을 갖게 되면 이를 사용하여 시스템에 침입할 수 있습니다.
...
Properties prop = new Properties();
prop.load(new FileInputStream("config.properties"));
String password = Base64.decode(prop.getProperty("password"));
DriverManager.getConnection(url, usr, password);
...
config.properties
에 액세스할 수 있는 모든 사용자는 password
값을 읽고 base64로 인코딩된 값임을 쉽게 알 수 있습니다. 비양심적인 직원이 이 정보에 대한 액세스 권한을 갖게 되면 이를 사용하여 시스템에 침입할 수 있습니다.
...
webview.setWebViewClient(new WebViewClient() {
public void onReceivedHttpAuthRequest(WebView view,
HttpAuthHandler handler, String host, String realm) {
String[] credentials = view.getHttpAuthUsernamePassword(host, realm);
String username = new String(Base64.decode(credentials[0], DEFAULT));
String password = new String(Base64.decode(credentials[1], DEFAULT));
handler.proceed(username, password);
}
});
...
...
obj = new XMLHttpRequest();
obj.open('GET','/fetchusers.jsp?id='+form.id.value,'true','scott','tiger');
...
plist
파일에서 암호를 읽고, 암호로 보호되는 파일의 압축을 풀 때 이 암호를 사용합니다.
...
NSDictionary *dict= [NSDictionary dictionaryWithContentsOfFile:[[NSBundle mainBundle] pathForResource:@"Config" ofType:@"plist"]];
NSString *encoded_password = [dict valueForKey:@"encoded_password"];
NSData *decodedData = [[NSData alloc] initWithBase64EncodedString:encoded_password options:0];
NSString *decodedString = [[NSString alloc] initWithData:decodedData encoding:NSUTF8StringEncoding];
[SSZipArchive unzipFileAtPath:zipPath toDestination:destPath overwrite:TRUE password:decodedString error:&error];
...
Config.plist
파일에 액세스할 수 있는 모든 사용자는 encoded_password
의 값을 읽고 base64로 인코딩된 값임을 쉽게 알 수 있습니다.
...
$props = file('config.properties', FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES);
$password = base64_decode($props[0]);
$link = mysql_connect($url, $usr, $password);
if (!$link) {
die('Could not connect: ' . mysql_error());
}
...
config.properties
에 액세스할 수 있는 모든 사용자는 password
값을 읽고 base64로 인코딩된 값임을 쉽게 알 수 있습니다. 비양심적인 직원이 이 정보에 대한 액세스 권한을 갖게 되면 이를 사용하여 시스템에 침입할 수 있습니다.
...
props = os.open('config.properties')
password = base64.b64decode(props[0])
link = MySQLdb.connect (host = "localhost",
user = "testuser",
passwd = password,
db = "test")
...
config.properties
에 액세스할 수 있는 모든 사용자는 password
값을 읽고 base64로 인코딩된 값임을 쉽게 알 수 있습니다. 비양심적인 직원이 이 정보에 대한 액세스 권한을 갖게 되면 이를 사용하여 시스템에 침입할 수 있습니다.
require 'pg'
require 'base64'
...
passwd = Base64.decode64(ENV['PASSWD64'])
...
conn = PG::Connection.new(:dbname => "myApp_production", :user => username, :password => passwd, :sslmode => 'require')
PASSWD64
의 값을 읽고 해당 값이 base64로 인코딩된 값임을 쉽게 알 수 있습니다. 비양심적인 직원이 이 정보에 대한 액세스 권한을 갖게 되면 이를 사용하여 시스템에 침입할 수 있습니다.
...
val prop = new Properties();
prop.load(new FileInputStream("config.properties"));
val password = Base64.decode(prop.getProperty("password"));
DriverManager.getConnection(url, usr, password);
...
config.properties
에 액세스할 수 있는 모든 사용자는 password
의 값을 읽고 base64로 인코딩된 값임을 쉽게 알 수 있습니다. 비양심적인 직원이 이 정보에 대한 접근 권한을 갖게 되면 이를 사용하여 시스템에 침입할 수 있습니다.plist
파일에서 암호를 읽고, 암호로 보호되는 파일의 압축을 풀 때 이 암호를 사용합니다.
...
var myDict: NSDictionary?
if let path = NSBundle.mainBundle().pathForResource("Config", ofType: "plist") {
myDict = NSDictionary(contentsOfFile: path)
}
if let dict = myDict {
let password = base64decode(dict["encoded_password"])
zipArchive.unzipOpenFile(zipPath, password:password])
}
...
Config.plist
파일에 액세스할 수 있는 모든 사용자는 encoded_password
의 값을 읽고 base64로 인코딩된 값임을 쉽게 알 수 있습니다.
...
root:qFio7llfVKk.s:19033:0:99999:7:::
...
...
...
Private Declare Function GetPrivateProfileString _
Lib "kernel32" Alias "GetPrivateProfileStringA" _
(ByVal lpApplicationName As String, _
ByVal lpKeyName As Any, ByVal lpDefault As String, _
ByVal lpReturnedString As String, ByVal nSize As Long, _
ByVal lpFileName As String) As Long
...
Dim password As String
...
password = StrConv(DecodeBase64(GetPrivateProfileString("MyApp", "Password", _
"", value, Len(value), _
App.Path & "\" & "Config.ini")), vbUnicode)
...
con.ConnectionString = "Driver={Microsoft ODBC for Oracle};Server=OracleServer.world;Uid=scott;Passwd=" & password &";"
...
Config.ini
에 액세스할 수 있는 모든 사용자는 Password
값을 읽고 base64로 인코딩된 값임을 쉽게 알 수 있습니다. 비양심적인 직원이 이 정보에 대한 액세스 권한을 갖게 되면 이를 사용하여 시스템에 침입할 수 있습니다.
...
*Get the report that is to be deleted
r_name = request->get_form_field( 'report_name' ).
CONCATENATE `C:\\users\\reports\\` r_name INTO dsn.
DELETE DATASET dsn.
...
..\\..\\usr\\sap\\DVEBMGS00\\exe\\disp+work.exe
" 등의 파일 이름을 지정하면 응용 프로그램이 중요 파일을 삭제하게 되므로 SPS 시스템이 즉시 중단됩니다.
...
PARAMETERS: p_date TYPE string.
*Get the invoice file for the date provided
CALL FUNCTION 'FILE_GET_NAME'
EXPORTING
logical_filename = 'INVOICE'
parameter_1 = p_date
IMPORTING
file_name = v_file
EXCEPTIONS
file_not_found = 1
OTHERS = 2.
IF sy-subrc <> 0.
* Implement suitable error handling here
ENDIF.
OPEN DATASET v_file FOR INPUT IN TEXT MODE.
DO.
READ DATASET v_file INTO v_record.
IF SY-SUBRC NE 0.
EXIT.
ELSE.
WRITE: / v_record.
ENDIF.
ENDDO.
...
..\\..\\usr\\sap\\sys\\profile\\default.pfl
" 등의 문자열을 지정하면 응용 프로그램은 모든 기본 SAP 응용 프로그램 서버 매개 변수 설정을 노출시키고, 이는 더욱 치밀한 공격으로 이어질 수 있습니다.../../tomcat/conf/server.xml
" 등의 파일 이름을 제공하여 응용 프로그램이 자신의 구성 파일을 삭제하게 만들 가능성을 고려하지 않았습니다.예제 2: 다음 코드는 구성 파일의 입력을 사용하여 열 파일을 결정하고 "디버그" 콘솔 또는 로그 파일에 씁니다. 프로그램이 일정한 권한으로 실행되고 악의적인 사용자가 구성 파일을 변경할 수 있는 경우, 이 프로그램을 사용하여 시스템에서 확장명이
var params:Object = LoaderInfo(this.root.loaderInfo).parameters;
var rName:String = String(params["reportName"]);
var rFile:File = new File("/usr/local/apfr/reports/" + rName);
...
rFile.deleteFile();
.txt
인 파일을 읽을 수 있습니다.
var fs:FileStream = new FileStream();
fs.open(new File(String(configStream.readObject())+".txt"), FileMode.READ);
fs.readBytes(arr);
trace(arr);
public class MyController {
...
public PageRerference loadRes() {
PageReference ref = ApexPages.currentPage();
Map<String,String> params = ref.getParameters();
if (params.containsKey('resName')) {
if (params.containsKey('resPath')) {
return PageReference.forResource(params.get('resName'), params.get('resPath'));
}
}
return null;
}
}
..\\..\\Windows\\System32\\krnl386.exe
" 등의 파일 이름을 제공하여 응용 프로그램이 중요한 Windows 시스템 파일을 삭제하게 만들 가능성을 고려하지 않았습니다.예제 2: 다음 코드는 구성 파일의 입력을 사용하여 열 파일을 결정하고 사용자에게 돌려 보냅니다. 프로그램이 충분한 권한으로 실행되고 악의적인 사용자가 구성 파일을 변경할 수 있는 경우, 이 프로그램을 사용하여 시스템에서 확장명이 ".txt"인 파일을 읽을 수 있습니다.
String rName = Request.Item("reportName");
...
File.delete("C:\\users\\reports\\" + rName);
sr = new StreamReader(resmngr.GetString("sub")+".txt");
while ((line = sr.ReadLine()) != null) {
Console.WriteLine(line);
}
../../apache/conf/httpd.conf
" 등의 파일 이름을 제공하여 응용 프로그램이 지정한 구성 파일을 삭제하게 만들 가능성을 고려하지 않았습니다.예제 2: 다음 코드는 명령줄의 입력을 사용하여 열 파일을 결정하고 사용자에게 돌려 보냅니다. 프로그램이 충분한 권한으로 실행되고 악의적인 사용자가 파일에 대한 소프트 링크를 만들게 되면 프로그램을 이용하여 시스템의 모든 파일의 첫 부분을 읽을 수 있습니다.
char* rName = getenv("reportName");
...
unlink(rName);
ifstream ifs(argv[0]);
string s;
ifs >> s;
cout << s;
...
EXEC CICS
WEB READ
FORMFIELD(FILE)
VALUE(FILENAME)
...
END-EXEC.
EXEC CICS
READ
FILE(FILENAME)
INTO(RECORD)
RIDFLD(ACCTNO)
UPDATE
...
END-EXEC.
...
..\\..\\Windows\\System32\\krnl386.exe
" 등의 파일 이름을 제공하여 응용 프로그램이 중요한 Windows 시스템 파일을 삭제하게 만들 가능성을 고려하지 않았습니다.
<cffile action = "delete"
file = "C:\\users\\reports\\#Form.reportName#">
final server = await HttpServer.bind('localhost', 18081);
server.listen((request) async {
final headers = request.headers;
final path = headers.value('path');
File(path!).delete();
}
Example 1
에서는 파일에 대한 삭제 기능을 수행하기 전에 headers.value('path')
의 유효성을 검사하지 않습니다.../../tomcat/conf/server.xml
" 등의 파일 이름을 제공하여 응용 프로그램이 자신의 구성 파일을 삭제하게 만들 가능성을 고려하지 않았습니다.예제 2: 다음 코드는 구성 파일의 입력을 사용하여 열 파일을 결정하고 사용자에게 돌려 보냅니다. 프로그램이 일정한 권한으로 실행되고 악의적인 사용자가 구성 파일을 변경할 수 있는 경우, 이 프로그램을 사용하여 시스템에서 확장명이
rName := "/usr/local/apfr/reports/" + req.FormValue("fName")
rFile, err := os.OpenFile(rName, os.O_RDWR|os.O_CREATE, 0755)
defer os.Remove(rName);
defer rFile.Close()
...
.txt
인 파일을 읽을 수 있습니다.
...
config := ReadConfigFile()
filename := config.fName + ".txt";
data, err := ioutil.ReadFile(filename)
...
fmt.Println(string(data))
../../tomcat/conf/server.xml
" 등의 파일 이름을 제공하여 응용 프로그램이 자신의 구성 파일을 삭제하게 만들 가능성을 고려하지 않았습니다.예제 2: 다음 코드는 구성 파일의 입력을 사용하여 열 파일을 결정하고 사용자에게 돌려 보냅니다. 프로그램이 일정한 권한으로 실행되고 악의적인 사용자가 구성 파일을 변경할 수 있는 경우, 이 프로그램을 사용하여 시스템에서 확장명이
String rName = request.getParameter("reportName");
File rFile = new File("/usr/local/apfr/reports/" + rName);
...
rFile.delete();
.txt
인 파일을 읽을 수 있습니다.
fis = new FileInputStream(cfg.getProperty("sub")+".txt");
amt = fis.read(arr);
out.println(arr);
Example 1
을 Android 플랫폼에 맞게 조정합니다.
...
String rName = this.getIntent().getExtras().getString("reportName");
File rFile = getBaseContext().getFileStreamPath(rName);
...
rFile.delete();
...
../../tomcat/conf/server.xml
" 등의 파일 이름을 제공하여 응용 프로그램이 자신의 구성 파일을 삭제하게 만들 가능성을 고려하지 않았습니다.예제 2: 다음 코드는 로컬 저장소의 입력을 사용하여 열 파일을 결정하고 사용자에게 돌려보냅니다. 악의적인 사용자가 로컬 저장소의 내용을 변경할 수 있는 경우, 이 프로그램을 사용하여 시스템에서 확장명이
...
var reportNameParam = "reportName=";
var reportIndex = document.indexOf(reportNameParam);
if (reportIndex < 0) return;
var rName = document.URL.substring(reportIndex+reportNameParam.length);
window.requestFileSystem(window.TEMPORARY, 1024*1024, function(fs) {
fs.root.getFile('/usr/local/apfr/reports/' + rName, {create: false}, function(fileEntry) {
fileEntry.remove(function() {
console.log('File removed.');
}, errorHandler);
}, errorHandler);
}, errorHandler);
.txt
인 파일을 읽을 수 있습니다.
...
var filename = localStorage.sub + '.txt';
function oninit(fs) {
fs.root.getFile(filename, {}, function(fileEntry) {
fileEntry.file(function(file) {
var reader = new FileReader();
reader.onloadend = function(e) {
var txtArea = document.createElement('textarea');
txtArea.value = this.result;
document.body.appendChild(txtArea);
};
reader.readAsText(file);
}, errorHandler);
}, errorHandler);
}
window.requestFileSystem(window.TEMPORARY, 1024*1024, oninit, errorHandler);
...
../../tomcat/conf/server.xml
" 등의 파일 이름을 제공하여 응용 프로그램이 자신의 구성 파일을 삭제하게 만들 가능성을 고려하지 않았습니다.예제 2: 다음 코드는 구성 파일의 입력을 사용하여 열 파일을 결정하고 사용자에게 돌려 보냅니다. 프로그램이 일정한 권한으로 실행되고 악의적인 사용자가 구성 파일을 변경할 수 있는 경우, 이 프로그램을 사용하여 시스템에서 확장명이
val rName: String = request.getParameter("reportName")
val rFile = File("/usr/local/apfr/reports/$rName")
...
rFile.delete()
.txt
인 파일을 읽을 수 있습니다.
fis = FileInputStream(cfg.getProperty("sub").toString() + ".txt")
amt = fis.read(arr)
out.println(arr)
Example 1
을 Android 플랫폼에 맞게 조정합니다.
...
val rName: String = getIntent().getExtras().getString("reportName")
val rFile: File = getBaseContext().getFileStreamPath(rName)
...
rFile.delete()
...
- (NSData*) testFileManager {
NSString *rootfolder = @"/Documents/";
NSString *filePath = [rootfolder stringByAppendingString:[fileName text]];
NSFileManager *fm = [NSFileManager defaultManager];
return [fm contentsAtPath:filePath];
}
../../tomcat/conf/server.xml
" 등의 파일 이름을 제공하여 응용 프로그램이 자신의 구성 파일을 삭제하게 만들 가능성을 고려하지 않았습니다.예제 2: 다음 코드는 구성 파일의 입력을 사용하여 열 파일을 결정하고 사용자에게 돌려 보냅니다. 프로그램이 일정한 권한으로 실행되고 악의적인 사용자가 구성 파일을 변경할 수 있는 경우, 이 프로그램을 사용하여 시스템에서 확장명이
$rName = $_GET['reportName'];
$rFile = fopen("/usr/local/apfr/reports/" . rName,"a+");
...
unlink($rFile);
.txt
인 파일을 읽을 수 있습니다.
...
$filename = $CONFIG_TXT['sub'] . ".txt";
$handle = fopen($filename,"r");
$amt = fread($handle, filesize($filename));
echo $amt;
...
../../tomcat/conf/server.xml
" 등의 파일 이름을 제공하여 응용 프로그램이 자신의 구성 파일을 삭제하게 만들 가능성을 고려하지 않았습니다.예제 2: 다음 코드는 구성 파일의 입력을 사용하여 열 파일을 결정하고 사용자에게 돌려 보냅니다. 프로그램이 일정한 권한으로 실행되고 악의적인 사용자가 구성 파일을 변경할 수 있는 경우, 이 프로그램을 사용하여 시스템에서 확장명이
rName = req.field('reportName')
rFile = os.open("/usr/local/apfr/reports/" + rName)
...
os.unlink(rFile);
.txt
인 파일을 읽을 수 있습니다.
...
filename = CONFIG_TXT['sub'] + ".txt";
handle = os.open(filename)
print handle
...
../../tomcat/conf/server.xml
" 등의 파일 이름을 제공하여 응용 프로그램이 자신의 구성 파일을 삭제하게 만들 가능성을 고려하지 않았습니다.예제 2: 다음 코드는 구성 파일의 입력을 사용하여 열 파일을 결정하고 사용자에게 돌려 보냅니다. 프로그램이 일정한 권한으로 실행되고 악의적인 사용자가 구성 파일을 변경할 수 있는 경우, 이 프로그램을 사용하여 시스템에서 확장명이
rName = req['reportName']
File.delete("/usr/local/apfr/reports/#{rName}")
.txt
인 파일을 읽을 수 있습니다.
...
fis = File.new("#{cfg.getProperty("sub")}.txt")
amt = fis.read
puts amt
../../tomcat/conf/server.xml
" 등의 파일 이름을 제공하여 응용 프로그램이 자신의 구성 파일을 삭제하게 만들 가능성을 고려하지 않았습니다.예제 2: 다음 코드는 구성 파일의 입력을 사용하여 열 파일을 결정하고 사용자에게 돌려 보냅니다. 프로그램이 일정한 권한으로 실행되고 악의적인 사용자가 구성 파일을 변경할 수 있는 경우, 이 프로그램을 사용하여 시스템에서 확장명이
def readFile(reportName: String) = Action { request =>
val rFile = new File("/usr/local/apfr/reports/" + reportName)
...
rFile.delete()
}
.txt
인 파일을 읽을 수 있습니다.
val fis = new FileInputStream(cfg.getProperty("sub")+".txt")
val amt = fis.read(arr)
out.println(arr)
func testFileManager() -> NSData {
let filePath : String = "/Documents/\(fileName.text)"
let fm : NSFileManager = NSFileManager.defaultManager()
return fm.contentsAtPath(filePath)
}
..\conf\server.xml
" 등의 파일 이름을 제공하여 응용 프로그램이 자신의 구성 파일을 삭제하게 만들 가능성을 고려하지 않았습니다.예제 2: 다음 코드는 구성 파일의 입력을 사용하여 열 파일을 결정하고 사용자에게 돌려 보냅니다. 프로그램이 일정한 권한으로 실행되고 악의적인 사용자가 구성 파일을 변경할 수 있는 경우, 이 프로그램을 사용하여 시스템에서 확장명이
Dim rName As String
Dim fso As New FileSystemObject
Dim rFile as File
Set rName = Request.Form("reportName")
Set rFile = fso.GetFile("C:\reports\" & rName)
...
fso.DeleteFile("C:\reports\" & rName)
...
.txt
인 파일을 읽을 수 있습니다.
Dim fileName As String
Dim tsContent As String
Dim ts As TextStream
Dim fso As New FileSystemObject
fileName = GetPrivateProfileString("MyApp", "sub", _
"", value, Len(value), _
App.Path & "\" & "Config.ini")
...
Set ts = fso.OpenTextFile(fileName,1)
tsContent = ts.ReadAll
Response.Write tsContent
...
Path.Combine
은 여러 파일 경로를 인수로 사용합니다. 일반적으로 파일에 대한 read()
또는 write()
호출 후에 여러 파일 경로를 연결하여 하나의 전체 경로를 가져옵니다. 첫 번째 또는 나머지 매개 변수가 절대 경로인지 여부에 따라 서로 다른 여러 시나리오가 설명서에 설명되어 있습니다. 두 번째 또는 나머지 매개 변수가 절대 경로인 경우 Path.Combine()
은 해당 절대 경로를 반환합니다. 이전 매개 변수는 무시됩니다. 이 경우 다음 예제와 유사한 코드가 있는 응용 프로그램에서 심각한 영향이 발생합니다.
// Called with user-controlled data
public static bytes[] getFile(String filename)
{
String imageDir = "\\FILESHARE\images\";
filepath = Path.Combine(imageDir, filename);
return File.ReadAllBytes(filepath);
}
C:\\inetpub\wwwroot\web.config
)를 제공하여 응용 프로그램이 반환하는 파일을 제어할 수 있습니다.
...
" Add Binary File to
CALL METHOD lr_abap_zip->add
EXPORTING
name = p_ifile
content = lv_bufferx.
" Read Binary File to
CALL METHOD lr_abap_zip->get
EXPORTING
name = p_ifile
IMPORTING
content = lv_bufferx2.
...
Example 1
에서는 이 항목 내의 데이터에 대한 읽기/쓰기 기능을 수행하기 전에 p_ifile
검증을 수행하지 않습니다. ZIP 파일이 Unix 기반 컴퓨터의 “/tmp/
” 디렉터리에 원래 배치되었고 ZIP 항목이 “../etc/hosts
”이며 응용 프로그램이 필요한 권한으로 실행되는 경우 이 파일이 시스템 hosts
파일을 덮어씁니다. 그러면 공격자는 컴퓨터의 트래픽을 공격자 컴퓨터로 다시 보내는 등 원하는 모든 위치로 전송할 수 있습니다.
public static void UnzipFile(ZipArchive archive, string destDirectory)
{
foreach (var entry in archive.Entries)
{
string file = entry.FullName;
if (!string.IsNullOrEmpty(file))
{
string destFileName = Path.Combine(destDirectory, file);
entry.ExtractToFile(destFileName, true);
}
}
}
Example 1
에서는 이 항목 내의 데이터에 대한 읽기/쓰기 작업을 수행하기 전에 entry.FullName
검증을 수행하지 않습니다. ZIP 파일이 원래 “C:\TEMP
” 디렉터리에 위치하고 ZIP 항목 이름에 “..\
세그먼트”가 포함되었으며 응용 프로그램이 필요한 권한으로 실행된 경우 시스템 파일을 임의로 덮어쓸 수 있습니다.
func Unzip(src string, dest string) ([]string, error) {
var filenames []string
r, err := zip.OpenReader(src)
if err != nil {
return filenames, err
}
defer r.Close()
for _, f := range r.File {
// Store filename/path for returning and using later on
fpath := filepath.Join(dest, f.Name)
filenames = append(filenames, fpath)
if f.FileInfo().IsDir() {
// Make Folder
os.MkdirAll(fpath, os.ModePerm)
continue
}
// Make File
if err = os.MkdirAll(filepath.Dir(fpath), os.ModePerm); err != nil {
return filenames, err
}
outFile, err := os.OpenFile(fpath, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, f.Mode())
if err != nil {
return filenames, err
}
rc, err := f.Open()
if err != nil {
return filenames, err
}
_, err = io.Copy(outFile, rc)
// Close the file without defer to close before next iteration of loop
outFile.Close()
rc.Close()
if err != nil {
return filenames, err
}
}
return filenames, nil
}
Example 1
에서는 이 항목 내의 데이터에 대한 읽기/쓰기 기능을 수행하기 전에 f.Name
검증을 수행하지 않습니다. ZIP 파일이 Unix 기반 컴퓨터의 “/tmp/
” 디렉터리에 원래 배치되었고 ZIP 항목이 “../etc/hosts
”이며 응용 프로그램이 필요한 권한으로 실행되는 경우 이 파일이 시스템 hosts
파일을 덮어씁니다. 그러면 공격자는 컴퓨터의 트래픽을 공격자 컴퓨터로 다시 보내는 등 원하는 모든 위치로 전송할 수 있습니다.
private static final int BUFSIZE = 512;
private static final int TOOBIG = 0x640000;
...
public final void unzip(String filename) throws IOException {
FileInputStream fis = new FileInputStream(filename);
ZipInputStream zis = new ZipInputStream(new BufferedInputStream(fis));
ZipEntry zipEntry = null;
int numOfEntries = 0;
long total = 0;
try {
while ((zipEntry = zis.getNextEntry()) != null) {
byte data[] = new byte[BUFSIZE];
int count = 0;
String outFileName = zipEntry.getName();
if (zipEntry.isDirectory()){
new File(outFileName).mkdir(); //create the new directory
continue;
}
FileOutputStream outFile = new FileOutputStream(outFileName);
BufferedOutputStream dest = new BufferedOutputStream(outFile, BUFSIZE);
//read data from Zip, but do not read huge entries
while (total + BUFSIZE <= TOOBIG && (count = zis.read(data, 0, BUFSIZE)) != -1) {
dest.write(data, 0, count);
total += count;
}
...
}
} finally{
zis.close();
}
}
...
Example 1
에서는 이 항목 내의 데이터에 대한 읽기/쓰기 기능을 수행하기 전에 zipEntry.getName()
검증을 수행하지 않습니다. ZIP 파일이 Unix 기반 컴퓨터의 “/tmp/
” 디렉터리에 원래 배치되었고 ZIP 항목이 “../etc/hosts
”이며 응용 프로그램이 필요한 권한으로 실행되는 경우 이 파일이 시스템 hosts
파일을 덮어씁니다. 그러면 공격자는 컴퓨터의 트래픽을 공격자 컴퓨터로 다시 보내는 등 원하는 모든 위치로 전송할 수 있습니다.
var unzipper = require('unzipper');
var fs = require('fs');
var untrusted_zip = getZipFromRequest();
fs.createReadStream(zipPath).pipe(unzipper.Extract({ path: 'out' }));
ZZArchive* archive = [ZZArchive archiveWithURL:[NSURL fileURLWithPath: zipPath] error:&error];
for (ZZArchiveEntry* entry in archive.entries) {
NSString *fullPath = [NSString stringWithFormat: @"%@/%@", destPath, [entry fileName]];
[[entry newDataWithError:nil] writeToFile:newFullPath atomically:YES];
}
Example 1
에서는 이 항목 내의 데이터에 대한 읽기/쓰기 기능을 수행하기 전에 entry.fileName
검증을 수행하지 않습니다. ZIP 파일이 iOS 응용 프로그램의 “Documents/hot_patches
” 디렉터리에 원래 배치되었고 ZIP 항목이 “../js/page.js
”인 경우 page.js
파일을 덮어씁니다. 이로 인해 공격자는 코드 실행을 야기할 수 있는 악성 코드를 삽입할 수 있게 됩니다.
...
$zip = new ZipArchive();
$zip->open("userdefined.zip", ZipArchive::RDONLY);
$zpm = $zip->getNameIndex(0);
$zip->extractTo($zpm);
...
Example 1
에서는 이 항목 내의 데이터에 대한 읽기/쓰기 기능을 수행하기 전에 f.Name
검증을 수행하지 않습니다. ZIP 파일이 Unix 기반 컴퓨터의 “/tmp/
” 디렉터리에 있고 ZIP 항목이 “../etc/hosts
”이며 응용 프로그램이 필요한 권한으로 실행되는 경우 이 파일이 시스템 hosts
파일을 덮어씁니다. 그러면 공격자는 컴퓨터의 트래픽을 공격자 컴퓨터로 다시 보내는 등 원하는 모든 위치로 전송할 수 있습니다.
import zipfile
import tarfile
def unzip(archive_name):
zf = zipfile.ZipFile(archive_name)
zf.extractall(".")
zf.close()
def untar(archive_name):
tf = tarfile.TarFile(archive_name)
tf.extractall(".")
tf.close()
예제 2: 다음 예제에서는 ZIP 파일에서 파일을 추출한 다음 안전하지 않은 방식으로 파일을 디스크에 씁니다.
import better.files._
...
val zipPath: File = getUntrustedZip()
val destinationPath = file"out/dest"
zipPath.unzipTo(destination = destinationPath)
import better.files._
...
val zipPath: File = getUntrustedZip()
val destinationPath = file"out/dest"
zipPath.newZipInputStream.mapEntries( (entry : ZipEntry) => {
entry.extractTo(destinationPath, new FileInputStream(entry.getName))
})
Example 2
에서는 이 항목 내의 데이터에 대한 읽기/쓰기 기능을 수행하기 전에 entry.getName
검증을 수행하지 않습니다. ZIP 파일이 Unix 기반 컴퓨터의 “/tmp/
” 디렉터리에 원래 배치되었고 ZIP 항목이 “../etc/hosts
”이며 응용 프로그램이 필요한 권한으로 실행되는 경우 이 파일이 시스템 hosts
파일을 덮어씁니다. 그러면 공격자는 컴퓨터의 트래픽을 공격자 컴퓨터로 다시 보내는 등 원하는 모든 위치로 전송할 수 있습니다.
let archive = try ZZArchive.init(url: URL(fileURLWithPath: zipPath))
for entry in archive.entries {
let fullPath = URL(fileURLWithPath: destPath + "/" + entry.fileName)
try entry.newData().write(to: fullPath)
}
Example 1
에서는 이 항목 내의 데이터에 대한 읽기/쓰기 기능을 수행하기 전에 entry.fileName
검증을 수행하지 않습니다. ZIP 파일이 iOS 응용 프로그램의 “Documents/hot_patches
” 디렉터리에 원래 배치되었고 ZIP 항목이 “../js/page.js
”인 경우 page.js
파일을 덮어씁니다. 이로 인해 공격자는 코드 실행을 야기할 수 있는 악성 코드를 삽입할 수 있게 됩니다.FileIOPermissions
를 결정합니다.
...
String permissionsXml = GetPermissionsFromXmlFile();
FileIOPermission perm = new FileIOPermission(PermissionState.None);
perm.FromXml(permissionsXml);
perm.Demand();
...
...
CrytoKeyAuditRule auditRule = new CryptoKeyAuditRule(IdRef, (CryptoKeyRights) input, AuditFlags.Success);
...
input
을 제어할 수 있는 경우 어떤 유형의 작업을 로그할 수 있는지 지정할 수 있습니다. 사용자가 이를 CryptoKeyRights.Delete
로 조작할 수 있는 경우 로그되지 않은 상태에서 암호화 키를 읽을 수 있으므로 공격자가 암호화 키를 도용한 사실을 알 수 없습니다.doExchange()
에서 드물게 발생하는 예외를 무시합니다.
try {
doExchange();
}
catch (RareException e) {
// this can never happen
}
RareException
이 발생하더라도 프로그램은 아무 문제가 발생하지 않은 것처럼 계속 실행됩니다. 즉, 프로그램이 특수 상황을 나타내는 증거를 기록하지 않으므로 나중에 프로그램 동작을 파악하기가 어려워질 수도 있습니다.DoExchange()
에서 아주 드물게 발생하는 예외 사항을 무시합니다.
try {
DoExchange();
}
catch (RareException e) {
// this can never happen
}
RareException
이 발생해도 프로그램은 아무 일도 없었던 것처럼 계속 실행됩니다. 프로그램은 특수한 상황을 나타내는 증거를 전혀 기록하지 않기 때문에 이후에 프로그램의 동작을 밝히려는 노력이 실패할 수 있습니다.doExchange()
에서 아주 드물게 발생하는 예외 사항을 무시합니다.
try {
doExchange();
}
catch (RareException e) {
// this can never happen
}
RareException
이 발생해도 프로그램은 아무 일도 없었던 것처럼 계속 실행됩니다. 프로그램은 특수한 상황을 나타내는 증거를 전혀 기록하지 않기 때문에 이후에 프로그램의 동작을 밝히려는 노력이 실패할 수 있습니다.doExchange()
에서 아주 드물게 발생하는 예외 사항을 무시합니다.
try {
doExchange();
}
catch (exception $e) {
// this can never happen
}
RareException
이 발생해도 프로그램은 아무 일도 없었던 것처럼 계속 실행됩니다. 프로그램은 특수한 상황을 나타내는 증거를 전혀 기록하지 않기 때문에 이후에 프로그램의 동작을 밝히려는 노력이 실패할 수 있습니다.open()
에서 아주 드물게 발생하는 예외 사항을 무시합니다.
try:
f = open('myfile.txt')
s = f.readline()
i = int(s.strip())
except:
# This will never happen
pass
RareException
이 발생해도 프로그램은 아무 일도 없었던 것처럼 계속 실행됩니다. 프로그램은 특수한 상황을 나타내는 증거를 전혀 기록하지 않기 때문에 이후에 프로그램의 동작을 밝히려는 노력이 실패할 수 있습니다.Exception
같은 높은 수준의 클래스를 catch하여 catch 블록을 "압축"하면 특수 처리가 필요하거나 프로그램의 이 시점에서 catch되지 않아야 하는 예외 사항을 숨길 수 있습니다. 지나치게 광범위한 예외 사항을 catch하면 .NET의 형식화된 예외 사항을 사용하는 의미가 사라지고 특히 프로그램이 커져서 새로운 형식의 예외 사항이 발생하기 시작하면 위험해질 수 있습니다. 새 예외 형식에는 주의를 기울이지 않기 때문입니다.
try {
DoExchange();
}
catch (IOException e) {
logger.Error("DoExchange failed", e);
}
catch (FormatException e) {
logger.Error("DoExchange failed", e);
}
catch (TimeoutException e) {
logger.Error("DoExchange failed", e);
}
try {
DoExchange();
}
catch (Exception e) {
logger.Error("DoExchange failed", e);
}
DoExchange()
가 수정되어 다른 방식으로 처리해야 하는 새로운 형식의 예외 사항이 발생하면 광범위한 catch 블록 때문에 컴파일러가 문제를 지적할 수 없습니다. 뿐만 아니라, 새 catch 블록은 ApplicationException
및 NullReferenceException
유형의 예외 사항도 처리하는데 이는 프로그래머의 의도와 반대되는 것입니다.Exception
같은 높은 수준의 클래스를 catch하여 catch 블록을 "압축"하면 특수 처리가 필요하거나 프로그램의 이 시점에서 catch되지 않아야 하는 예외 사항을 숨길 수 있습니다. 지나치게 광범위한 예외 사항을 catch하면 Java의 형식화된 예외 사항을 사용하는 의미가 사라지고 특히 프로그램이 커져서 새로운 형식의 예외 사항이 발생하기 시작하면 위험해질 수 있습니다. 새 예외 형식에는 주의를 기울이지 않기 때문입니다.
try {
doExchange();
}
catch (IOException e) {
logger.error("doExchange failed", e);
}
catch (InvocationTargetException e) {
logger.error("doExchange failed", e);
}
catch (SQLException e) {
logger.error("doExchange failed", e);
}
try {
doExchange();
}
catch (Exception e) {
logger.error("doExchange failed", e);
}
doExchange()
가 수정되어 다른 방식으로 처리해야 하는 새로운 형식의 예외 사항이 발생하면 광범위한 catch 블록 때문에 컴파일러가 문제를 지적할 수 없습니다. 뿐만 아니라, 새 catch 블록은 ClassCastException
및 NullPointerException
과 같이 RuntimeException
에서 파생된 예외 사항도 처리하는데 이는 프로그래머의 의도와 반대되는 것입니다.NullReferenceException
을 캐치(catch)하는 것은 일반적으로 잘못된 관행입니다.NullReferenceException
을 캐치(catch)합니다.NullReferenceException
을 발생시켜 오류 조건을 알립니다.NullReferenceException
을 캐치하는 오류를 범합니다.
try {
MysteryMethod();
}
catch (NullReferenceException npe) {
}
Console.Out
또는 Console.Error
를 사용하면 프로그램의 동작을 모니터링하기가 어렵습니다.
public class MyClass {
...
Console.WriteLine("hello world");
...
}
Console.WriteLine()
을 사용하여 표준 출력에 대한 메시지를 쓰는 것을 그만두지 않습니다.Console.WriteLine
사용이 발견되면 이는 구조화된 로깅 시스템으로 옮겨가는 도중 실수한 것일 수 있습니다.os.Stdout
또는 os.Stderr
를 사용하면 프로그램의 동작을 모니터링하기가 어렵습니다.
...
func foo(){
fmt.Println("Hello World")
}
fmt.Println()
을 사용하여 표준 출력에 메시지를 쓰는 것을 그만두지 않습니다.os.Stdout
또는 os.Stderr
로깅은 구조화된 로깅 시스템으로 옮겨가는 도중 실수한 것일 수 있습니다.System.out
또는 System.err
를 사용하면 프로그램의 동작을 모니터링하기가 어렵습니다.
public class MyClass
...
System.out.println("hello world");
...
}
System.out.println()
을 사용하여 표준 출력에 메시지를 쓰는 것을 그만두지 않습니다.System.out
또는 System.err
사용이 발견되면 이는 구조화된 로깅 시스템으로 옮겨가는 도중 실수한 것일 수 있습니다.process.stdout
또는 process.stderr
를 사용하면 프로그램의 동작을 모니터링하기가 어렵습니다.
process.stdin.on('readable', function(){
var s = process.stdin.read();
if (s != null){
process.stdout.write(s);
}
});
process.stdout.write()
를 사용하여 표준 출력에 메시지를 쓰는 것을 그만두지 않습니다.process.stdout
또는 process.stderr
사용이 발견되면 이는 구조화된 로깅 시스템으로 옮겨가는 도중 실수한 것일 수 있습니다.print
또는 println
를 사용하면 프로그램의 동작을 모니터링하기가 어렵습니다.
class MyClass {
...
println("hello world")
...
}
}
print
또는 println
를 사용하여 표준 출력에 메시지를 쓰는 것을 그만두지 않습니다.
sys.stdout.write("hello world")
sys.stdout
또는 sys.stderr
사용이 발견되면 이는 구조화된 로깅 시스템으로 옮겨가는 도중 실수한 것일 수 있습니다.Kernel.puts
,Kernel.warn
또는 Kernel.printf
를 사용하면 프로그램의 동작을 모니터링하기가 어렵습니다.
...
puts "hello world"
...
Kernel.puts
을 사용하여 표준 출력에 메시지를 쓰는 것을 그만두지 않습니다.Kernel.puts
,Kernel.warn
또는 Kernel.printf
사용이 발견되면 이는 구조화된 로깅 시스템으로 옮겨가는 도중 실수한 것일 수 있습니다.Logger
클래스를 사용하지만 시스템 출력 스트림에 정보를 기록합니다.
require 'logger'
...
logger = Logger.new($stdout)
logger.info("hello world")
...
...
var file:File = new File(directoryName + "\\" + fileName);
...
...
FileStream f = File.Create(directoryName + "\\" + fileName);
...
...
File file = new File(directoryName + "\\" + fileName);
...
...
os.open(directoryName + "\\" + fileName);
...
...
uid = 'scott'.
password = 'tiger'.
WRITE: / 'Default username for FTP connection is: ', uid.
WRITE: / 'Default password for FTP connection is: ', password.
...
pass = getPassword();
...
trace(id+":"+pass+":"+type+":"+tstamp);
Example 1
의 코드는 일반 텍스트 암호를 파일 시스템에 기록합니다. 많은 개발자가 파일 시스템을 안전한 데이터 저장소로 신뢰하지만 무조건 신뢰해서는 안 됩니다. 특히 개인 정보가 관련된 경우가 대표적입니다.
...
ResetPasswordResult passRes = System.resetPassword(id1, true);
System.Debug('New password: '+passRes.getPassword());
...
pass = GetPassword();
...
dbmsLog.WriteLine(id+":"+pass+":"+type+":"+tstamp);
Example 1
의 코드는 일반 텍스트 암호를 파일 시스템에 기록합니다. 많은 개발자가 파일 시스템을 안전한 데이터 저장소로 신뢰하지만 무조건 신뢰해서는 안 됩니다. 특히 개인 정보가 관련된 경우가 대표적입니다.get_password()
함수는 저장되는 값 중 사용자가 제공하는 계정 관련 일반 텍스트 암호를 반환합니다.
pass = get_password();
...
fprintf(dbms_log, "%d:%s:%s:%s", id, pass, type, tstamp);
Example 1
의 코드는 일반 텍스트 암호를 파일 시스템에 기록합니다. 많은 개발자가 파일 시스템을 모든 데이터의 안전한 저장소로 보고 신뢰하지만 무조건 신뢰해서는 안 됩니다. 특히 개인 정보가 관련된 경우가 대표적입니다.
...
MOVE "scott" TO UID.
MOVE "tiger" TO PASSWORD.
DISPLAY "Default username for database connection is: ", UID.
DISPLAY "Default password for database connection is: ", PASSWORD.
...
Session.pword
변수는 계정 관련 일반 텍스트 암호를 포함합니다.
<cflog file="app_log" application="No" Thread="No"
text="#Session.uname#:#Session.pword#:#type#:#Now()#">
Example 1
의 코드는 일반 텍스트 암호를 파일 시스템에 기록합니다. 많은 개발자가 파일 시스템을 안전한 데이터 저장소로 신뢰하지만 무조건 신뢰해서는 안 됩니다. 특히 개인 정보가 관련된 경우가 대표적입니다.
var pass = getPassword();
...
dbmsLog.println(id+":"+pass+":"+type+":"+tstamp);
Example 1
의 코드는 일반 텍스트 암호를 파일 시스템에 기록합니다. 많은 개발자가 파일 시스템을 안전한 데이터 저장소로 신뢰하지만 무조건 신뢰해서는 안 됩니다. 특히 개인 정보가 관련된 경우가 대표적입니다.GetPassword()
함수의 반환 값입니다.
pass = GetPassword();
...
if err != nil {
log.Printf('%s: %s %s %s', id, pass, type, tsstamp)
}
Example 1
의 코드는 일반 텍스트 암호를 응용 프로그램 이벤트 로그에 기록합니다. 많은 개발자가 이벤트 로그를 안전한 데이터 저장소로 신뢰하지만 무조건 신뢰해서는 안 됩니다. 특히 개인 정보가 관련된 경우가 대표적입니다.
pass = getPassword();
...
dbmsLog.println(id+":"+pass+":"+type+":"+tstamp);
Example 1
의 코드는 일반 텍스트 암호를 파일 시스템에 기록합니다. 많은 개발자가 파일 시스템을 안전한 데이터 저장소로 신뢰하지만 무조건 신뢰해서는 안 됩니다. 특히 개인 정보가 관련된 경우가 대표적입니다.
...
webview.setWebViewClient(new WebViewClient() {
public void onReceivedHttpAuthRequest(WebView view,
HttpAuthHandler handler, String host, String realm) {
String[] credentials = view.getHttpAuthUsernamePassword(host, realm);
String username = credentials[0];
String password = credentials[1];
Intent i = new Intent();
i.setAction("SEND_CREDENTIALS");
i.putExtra("username", username);
i.putExtra("password", password);
view.getContext().sendBroadcast(i);
}
});
...
SEND_CREDENTIALS
작업을 통해 인텐트를 수신 대기하도록 등록된 모든 수신자가 메시지를 수신합니다. 이 경우 수정 방법으로 권한을 사용하지 않는 것이 좋지만, 브로드캐스트는 수신자 수를 제한하는 권한으로도 보호되지 않습니다.
localStorage.setItem('password', password);
pass = getPassword()
...
dbmsLog.println("$id:$pass:$type:$tstamp")
Example 1
의 코드는 일반 텍스트 암호를 파일 시스템에 기록합니다. 많은 개발자가 파일 시스템을 안전한 데이터 저장소로 신뢰하지만 무조건 신뢰해서는 안 됩니다. 특히 개인 정보가 관련된 경우가 대표적입니다.
...
webview.webViewClient = object : WebViewClient() {
override fun onReceivedHttpAuthRequest(view: WebView,
handler: HttpAuthHandler, host: String, realm: String
) {
val credentials = view.getHttpAuthUsernamePassword(host, realm)
val username = credentials!![0]
val password = credentials[1]
val i = Intent()
i.action = "SEND_CREDENTIALS"
i.putExtra("username", username)
i.putExtra("password", password)
view.context.sendBroadcast(i)
}
}
...
SEND_CREDENTIALS
작업을 통해 인텐트를 수신 대기하도록 등록된 모든 수신자가 메시지를 수신합니다. 이 경우 수정 방법으로 권한을 사용하지 않는 것이 좋지만, 브로드캐스트는 수신자 수를 제한하는 권한으로도 보호되지 않습니다.
locationManager = [[CLLocationManager alloc] init];
locationManager.delegate = self;
locationManager.desiredAccuracy = kCLLocationAccuracyBest;
locationManager.distanceFilter = kCLDistanceFilterNone;
[locationManager startUpdatingLocation];
CLLocation *location = [locationManager location];
// Configure the new event with information from the location
CLLocationCoordinate2D coordinate = [location coordinate];
NSString *latitude = [NSString stringWithFormat:@"%f", coordinate.latitude];
NSString *longitude = [NSString stringWithFormat:@"%f", coordinate.longitude];
NSLog(@"dLatitude : %@", latitude);
NSLog(@"dLongitude : %@",longitude);
NSString *urlWithParams = [NSString stringWithFormat:TOKEN_URL, latitude, longitude];
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:urlWithParams]];
[request setHTTPMethod:@"GET"];
[[NSURLConnection alloc] initWithRequest:request delegate:self];
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
// Add password to user defaults
[defaults setObject:@"Super Secret" forKey:@"passwd"];
[defaults synchronize];
getPassword()
함수의 반환 값도 있습니다.
<?php
$pass = getPassword();
trigger_error($id . ":" . $pass . ":" . $type . ":" . $tstamp);
?>
Example 1
의 코드는 일반 텍스트 암호를 응용 프로그램 이벤트 로그에 기록합니다. 많은 개발자가 이벤트 로그를 안전한 데이터 저장소로 신뢰하지만 무조건 신뢰해서는 안 됩니다. 특히 개인 정보가 관련된 경우가 대표적입니다.OWA_SEC.get_password()
함수는 사용자가 제공하는 계정 관련 일반 텍스트 비밀번호를 반환하며 이는 HTTP 응답에 인쇄됩니다.
...
HTP.htmlOpen;
HTP.headOpen;
HTP.title (.Account Information.);
HTP.headClose;
HTP.bodyOpen;
HTP.br;
HTP.print('User ID: ' ||
OWA_SEC.get_user_id || '');
HTP.print('User Password: ' ||
OWA_SEC.get_password || '');
HTP.br;
HTP.bodyClose;
HTP.htmlClose;
...
getPassword()
함수의 반환 값도 있습니다.
pass = getPassword();
logger.warning('%s: %s %s %s', id, pass, type, tsstamp)
Example 1
의 코드는 일반 텍스트 암호를 응용 프로그램 이벤트 로그에 기록합니다. 많은 개발자가 이벤트 로그를 안전한 데이터 저장소로 신뢰하지만 무조건 신뢰해서는 안 됩니다. 특히 개인 정보가 관련된 경우가 대표적입니다.get_password()
함수는 저장되는 값 중 사용자가 제공하는 계정 관련 일반 텍스트 암호를 반환합니다.
pass = get_password()
...
dbms_logger.warn("#{id}:#{pass}:#{type}:#{tstamp}")
Example 1
의 코드는 일반 텍스트 암호를 파일 시스템에 기록합니다. 많은 개발자가 파일 시스템을 안전한 데이터 저장소로 신뢰하지만 무조건 신뢰해서는 안 됩니다. 특히 개인 정보가 관련된 경우가 대표적입니다.
val pass = getPassword()
...
dbmsLog.println(id+":"+pass+":"+type+":"+tstamp)
Example 1
의 코드는 일반 텍스트 암호를 파일 시스템에 기록합니다. 많은 개발자가 파일 시스템을 안전한 데이터 저장소로 신뢰하지만 무조건 신뢰해서는 안 됩니다. 특히 개인 정보가 관련된 경우가 대표적입니다.
import CoreLocation
...
var locationManager : CLLocationManager!
var seenError : Bool = false
var locationFixAchieved : Bool = false
var locationStatus : NSString = "Not Started"
seenError = false
locationFixAchieved = false
locationManager = CLLocationManager()
locationManager.delegate = self
locationManager.locationServicesEnabled
locationManager.desiredAccuracy = kCLLocationAccuracyBest
locationManager.startUpdatingLocation()
...
if let location: CLLocation! = locationManager.location {
var coordinate : CLLocationCoordinate2D = location.coordinate
let latitude = NSString(format:@"%f", coordinate.latitude)
let longitude = NSString(format:@"%f", coordinate.longitude)
NSLog("dLatitude : %@", latitude)
NSLog("dLongitude : %@",longitude)
let urlString : String = "http://myserver.com/?lat=\(latitude)&lon=\(longitude)"
let url : NSURL = NSURL(string:urlString)
let request : NSURLRequest = NSURLRequest(URL:url)
var err : NSError?
var response : NSURLResponse?
var data : NSData = NSURLConnection.sendSynchronousRequest(request, returningResponse: &response, error:&err)
} else {
println("no location...")
}
let defaults : NSUserDefaults = NSUserDefaults.standardUserDefaults()
// Add password to user defaults
defaults.setObject("Super Secret" forKey:"passwd")
defaults.synchronize()
getPassword
함수는 저장되는 값 중 사용자가 제공하는 계정 관련 일반 텍스트 암호를 반환합니다.
pass = getPassword
...
App.EventLog id & ":" & pass & ":" & type & ":" &tstamp, 4
...
Example 1
의 코드는 일반 텍스트 암호를 응용 프로그램 이벤트 로그에 기록합니다. 많은 개발자가 이벤트 로그를 안전한 데이터 저장소로 신뢰하지만 무조건 신뢰해서는 안 됩니다. 특히 개인 정보가 관련된 경우가 대표적입니다.
...
HKHealthStore healthStore = new HKHealthStore();
HKBloodTypeObject blood = healthStore.GetBloodType(null);
NSLog("%@", blood.BloodType);
var urlWithParams = String.format(TOKEN_URL, block.BloodType);
var responseString = await client.GetStringAsync(urlWithParams);
...
NSLog
함수보다 저수준인 Apple Logging API를 사용하면 개발자는 장치에서 모든 로그를 읽을 수 있는 응용 프로그램을 생성할 수 있습니다(다른 응용 프로그램을 소유하지 않은 경우에도).
...
HKHealthStore healthStore = new HKHealthStore();
HKBloodTypeObject blood = healthStore.GetBloodType(null);
// Add blood type to user defaults
NSUserDefaults.StandardUserDefaults.SetString(blood.BloodType, "bloodType");
...
...
HKHealthStore *healthStore = [[HKHealthStore alloc] init];
HKBloodTypeObject *blood = [healthStore bloodTypeWithError:nil];
NSLog(@"%@", [blood bloodType]);
NSString *urlWithParams = [NSString stringWithFormat:TOKEN_URL, [blood bloodType]];
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:urlWithParams]];
[request setHTTPMethod:@"GET"];
[[NSURLConnection alloc] initWithRequest:request delegate:self];
...
NSLog
함수보다 저수준인 Apple Logging API를 사용하면 개발자는 장치에서 모든 로그를 읽을 수 있는 응용 프로그램을 만들 수 있습니다(다른 응용 프로그램을 소유하지 않은 경우에도).
...
HKHealthStore *healthStore = [[HKHealthStore alloc] init];
HKBloodTypeObject *blood = [healthStore bloodTypeWithError:nil];
// Add blood type to user defaults
[defaults setObject:[blood bloodType] forKey:@"bloodType"];
[defaults synchronize];
...
...
let healthStore = HKHealthStore()
let blood = try healthStore.bloodType()
print(blood.bloodType)
let urlString : String = "http://myserver.com/?data=\(blood.bloodType)"
let url : NSURL = NSURL(string:urlString)
let request : NSURLRequest = NSURLRequest(URL:url)
var err : NSError?
var response : NSURLResponse?
var data : NSData = NSURLConnection.sendSynchronousRequest(request, returningResponse: &response, error:&err)
...
NSLog
함수보다 저수준인 Apple Logging API를 사용하면 개발자는 장치에서 모든 로그를 읽을 수 있는 응용 프로그램을 만들 수 있습니다(다른 응용 프로그램을 소유하지 않은 경우에도).
...
let healthStore = HKHealthStore()
let blood = try healthStore.bloodType()
print(blood.bloodType)
// Add blood type to user defaults
defaults.setObject("BloodType" forKey:blood.bloodType)
defaults.synchronize()
...
String
개체에 저장합니다.
public static String getPassword() {
String inputPassword = "";
ConsoleKeyInfo nextKey = Console.ReadKey(true);
while (nextKey.Key != Console.ReadKey(true)) {
inputPassword.AppendChar(nextKey.KeyChar);
Console.Write("*");
nextKey = Console.ReadKey(true);
}
return inputPassword;
}
String
은 변경이 불가능한 개체이므로 콘텐츠를 null로 만들 수 없습니다. 즉, 가비지 수집 전에 힙을 검사할 수 있는 모든 사용자에게 중요한 데이터가 공개됩니다.String
개체에 민감한 데이터를 저장하면 메모리에서 데이터를 완전히 없앨 수 없습니다.String
개체는 변경이 불가능하므로 중요한 데이터를 저장하는 데 중요한 데이터를 저장할 때 String
을 사용하는 경우가 많습니다. 메모리에서 String
의 값을 제거하는 작업은 JVM 가비지 수집기만 수행할 수 있습니다. 그런데 JVM의 메모리가 부족하지 않으면 가비지 수집기를 실행할 필요가 없으므로 가비지 수집이 수행되는 시기가 보장되지 않습니다. 응용 프로그램 충돌 시에는 응용 프로그램의 메모리 덤프로 인해 중요한 데이터가 노출될 수 있습니다.String
으로 변환합니다.
private JPasswordField pf;
...
final char[] password = pf.getPassword();
...
String passwordAsString = new String(password);
String
개체에 민감한 데이터를 저장하면 메모리에서 데이터를 완전히 없앨 수 없습니다.String
은 민감한 데이터를 저장하는 데 사용되지만 String
개체는 불변이므로 이 개체에 새 값을 할당하면 새 String
이 생성되고 해당 참조가 할당하려는 개체에 할당됩니다. 원래 값은 ARC
(Automatic Reference Counting)가 개체를 할당 해제하고 메모리를 해제할 때까지 메모리에 유지됩니다. Swift는 개체를 포함하는 가장 인접한 범위가 끝날 때까지 개체의 수명을 보장하지 않습니다. 개체가 할당 해제되기 전에 공격자가 메모리의 콘텐트를 덤프하게 되면 콘텐트를 읽을 수 있습니다.String
을 사용하여 메모리에 암호를 저장합니다.
let password = passwordTextField.text!
// use the password
SensitiveXMLData
XML 데이터 소스의 민감한 재무 정보 그래프를 생성하는 DataVisualization
제어를 인스턴스화할 수 있습니다.
<asp:Chart ID="Chart1" runat="server" ImageLocation="~/Temporary/Graph"
ImageType="Jpeg" DataSourceID="SensitiveXMLData" ImageStorageMode="UseImageLocation">
<series>
.
.
.
</series>
<chartareas>
<asp:ChartArea Name="ChartArea1">
</asp:ChartArea>
</chartareas>
</asp:Chart>
Example 1
의 코드는 Chart
제어가 막대 그래프의 JPEG 이미지를 생성하고 이를 임시 디렉터리인 ~/Temporary/Graph
에 작성하도록 지시합니다. 제어가 디스크에 이미지를 작성하면 사용자의 브라우저는 이후의 파일 요청을 보내 사용자에게 표시합니다. 이미지가 디스크에 안전하게 작성되지 않습니다. 또한, 코드는 기본 인프라가 다른 사용자에 의한 무단 액세스로부터 파일을 보호할 것이라고 가정합니다.DataType
이 password로 지정되어 있지 않으므로 표시할 때 기본적으로 표시됩니다.
public class User
{
[Required]
public int ID { get; set; }
public string Title { get; set; }
[DataType(DataType.Date)]
[DisplayFormat(DataFormatString = "{0:yyyy-MM-dd}", ApplyFormatInEditMode = true)]
public DateTime DateOfEmployment { get; set; }
[DataType(DataType.Currency)]
public decimal Salary { get; set; }
[Required]
public string Username { get; set; }
[Required]
public string Password { get; set; }
...
}
Example 1
에서 Password
속성은 [DataType(DataType.Password)]
속성을 지정하지 않았으므로 UI에 표시될 때 기본적으로 숨겨지지 않습니다.TextField
위젯이 숨김 처리하지 않음을 보여 줍니다.
class SelectionContainerDisabledExampleApp extends StatelessWidget {
const SelectionContainerDisabledExampleApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
body: Center(
child: Column(
children: <Widget>[
TextField(
decoration: InputDecoration(
hintText: "Please enter your password",
),
),
],
),
),
),
);
}
}
Example 1
에서는 obscureText
속성을 true
로 설정하여 TextField
위젯을 인스턴스화하지 않았으므로 사용자가 "비밀번호를 입력하십시오." 프롬프트에 입력하는 비밀번호가 숨김 처리되지 않습니다.PasswordCallback pc = new PasswordCallback("Please enter your password: ", true);
예제 1
의 pc
는 두 번째 매개 변수인 onEcho
가 true
로 설정된 상태로 인스턴스화되었으므로 사용자가 "비밀번호를 입력하십시오." 프롬프트에 입력하는 비밀번호가 숨김 처리되지 않습니다.
ViewController.h:
...
@property (nonatomic, retain) IBOutlet UITextField *passwordField;
...
ViewController.m:
...
NSString *password = _passwordField.text;
...
예제 1
의 passwordField
는 secureTextEntry
속성을 true
로 설정하지 않았기 때문에 사용자가 텍스트 필드에 입력하는 비밀번호가 숨김 처리되지 않습니다.
...
@IBOutlet weak var passwordField: UITextField!
...
let password = passwordField.text
...
예제 1
의 passwordField
는 secureTextEntry
속성을 true
로 설정하지 않았기 때문에 사용자가 텍스트 필드에 입력하는 비밀번호가 숨김 처리되지 않습니다.
...
tid = request->get_form_field( 'tid' ).
CALL TRANSACTION tid USING bdcdata MODE 'N'
MESSAGES INTO messtab.
...
APPHOME
을 사용하고 그런 다음 지정된 디렉터리에서 상대 경로를 사용하여 기본 라이브러리를 로드합니다.
...
string lib = ConfigurationManager.AppSettings["APPHOME"];
Environment.ExitCode = AppDomain.CurrentDomain.ExecuteAssembly(lib);
...
APPHOME
을 LIBNAME
의 악성 버전이 들어 있는 다른 경로를 가리키도록 수정하기 때문에 공격자가 라이브러리 또는 실행 파일을 로드하고 응용 프로그램에 대한 높은 권한으로 임의의 코드를 실행할 수도 있습니다. 프로그램이 환경에서 읽은 값을 확인하지 않기 때문에, 공격자가 시스템 속성 APPHOME
의 값을 제어할 수 있는 경우 응용 프로그램을 조작하여 악성 코드를 실행하게 하고 시스템을 제어할 수 있습니다.
...
RegQueryValueEx(hkey, "APPHOME",
0, 0, (BYTE*)home, &size);
char* lib=(char*)malloc(strlen(home)+strlen(INITLIB));
if (lib) {
strcpy(lib,home);
strcat(lib,INITCMD);
LoadLibrary(lib);
}
...
INITLIB
의 악성 버전이 들어 있는 다른 경로를 지정하는 방식으로 높은 응용 프로그램 권한으로 이 라이브러리에서 코드를 실행합니다. 프로그램이 환경에서 읽은 값을 확인하지 않기 때문에 공격자가 APPHOME
의 값을 제어하게 되면 응용 프로그램이 악성 코드를 실행하게 만들 수 있습니다.liberty.dll
이라는 라이브러리를 사용합니다.
LoadLibrary("liberty.dll");
liberty.dll
l의 절대 경로를 지정하지 않습니다. 공격자가 검색 순서에서 원하는 파일보다 앞쪽에 liberty.dll
이라는 악성 라이브러리를 배치하고 웹 서버 환경이 아닌 자신의 환경에서 프로그램을 실행할 방법을 찾게 되면 응용 프로그램은 신뢰할 수 있는 라이브러리 대신 악성 라이브러리를 로드합니다. 이런 종류의 응용 프로그램은 높은 권한으로 실행되기 때문에 공격자의 liberty.dll
의 내용도 높은 권한으로 실행되고 공격자가 시스템을 완전히 장악할 수 있습니다.LoadLibrary()
가 사용하는 검색 순서 때문에 발생합니다. Windows 최신 버전까지 그래왔듯이 시스템 디렉터리보다 현재 디렉터리를 먼저 검색하면 공격자가 프로그램을 로컬로 수행할 수 있어도 이런 종류의 공격은 하찮은 것이 됩니다. 검색 순서는 운영 체제 버전에 따라 결정되고 최신 운영 체제에서 다음 레지스트리 키의 값에 따라 제어됩니다.
HKLM\System\CurrentControlSet\Control\Session Manager\SafeDllSearchMode
LoadLibrary()
는 다음과 같이 동작합니다.SafeDllSearchMode
가 1인 경우 검색 순서는 다음과 같습니다.PATH
환경 변수에 나열된 디렉터리.SafeDllSearchMode
가 0인 경우 검색 순서는 다음과 같습니다.PATH
환경 변수에 나열된 디렉터리.
...
ACCEPT PROGNAME.
EXEC CICS
LINK PROGRAM(PROGNAME)
COMMAREA(COMA)
LENGTH(LENA)
DATALENGTH(LENI)
SYSID('CONX')
END-EXEC.
...
APPHOME
을 사용하여 코드가 설치되는 디렉터리를 결정한 다음 특정 디렉터리 기준 상대 경로를 사용하여 네이티브 라이브러리를 로드합니다.
...
String home = System.getProperty("APPHOME");
String lib = home + LIBNAME;
java.lang.Runtime.getRuntime().load(lib);
...
APPHOME
을 LIBNAME
의 악성 버전이 들어 있는 다른 경로를 가리키도록 수정하기 때문에 공격자가 라이브러리를 로드하고 응용 프로그램에 대한 높은 권한으로 임의의 코드를 실행할 수도 있습니다. 프로그램이 환경에서 읽은 값을 확인하지 않기 때문에, 공격자가 시스템 속성 APPHOME
의 값을 제어할 수 있는 경우 응용 프로그램을 조작하여 악성 코드를 실행하게 하고 시스템을 제어할 수 있습니다. System.loadLibrary()
를 사용하여 보통 표준 시스템 디렉터리에 있는 library.dll
이라는 기본 라이브러리에서 코드를 로드합니다.
...
System.loadLibrary("library.dll");
...
System.loadLibrary()
가 로드할 라이브러리에 대해 경로가 아닌 라이브러리 이름을 받는다는 점입니다. Java 1.4.2 API 설명서에 따르면 이 함수는 다음과 같이 동작합니다[1]:library.dll
의 악성 복사본을 배치하게 되면 응용 프로그램은 의도한 파일이 아닌 악성 복사본을 로드합니다. 응용 프로그램은 그 속성 때문에 높은 권한으로 실행됩니다. 즉, 공격자의 library.dll
의 내용이 높은 권한으로 실행되어 공격자에게 시스템의 완전한 제어권을 넘겨줄 수 있습니다.Express
의 현재 문서화되지 않은 "기능" 을 사용하여 동적으로 라이브러리 파일을 로드합니다. 그런 다음 Node.js
는 계속해서 이 라이브러리([1])가 포함된 파일 또는 디렉터리에 대한 일반 라이브러리 로드 경로를 검색합니다.
var express = require('express');
var app = express();
app.get('/', function(req, res, next) {
res.render('tutorial/' + req.params.page);
});
Express
에서 Response.render()
로 전달된 페이지는 이전에 알 수 없을 때 확장의 라이브러리를 로드합니다. 이는 잘 알려진 템플릿 엔진인 pug
라이브러리 로드를 의미하기 때문에 일반적으로 "foo.pug"와 같은 입력에 대해 문제가 되지 않습니다. 하지만 공격자가 페이지와 확장을 제어할 수 있는 경우 공격자는 Node.js
모듈 로드 경로 내의 모든 라이브러리를 로드하도록 선택할 수 있습니다. 프로그램이 URL 매개 변수에서 수신한 정보를 검증하지 않기 때문에 공격자는 응용 프로그램을 조작하여 악성 코드를 실행하게 하고 시스템을 제어할 수 있습니다.APPHOME
을 사용하여 코드가 설치되는 디렉터리를 결정한 다음 특정 디렉터리 기준 상대 경로를 사용하여 네이티브 라이브러리를 로드합니다.
...
$home = getenv("APPHOME");
$lib = $home + $LIBNAME;
dl($lib);
...
APPHOME
을 LIBNAME
의 악성 버전이 들어 있는 다른 경로를 가리키도록 수정하기 때문에 공격자가 라이브러리를 로드하고 응용 프로그램에 대한 높은 권한으로 임의의 코드를 실행할 수도 있습니다. 프로그램이 환경에서 읽은 값을 확인하지 않기 때문에, 공격자가 시스템 속성 APPHOME
의 값을 제어할 수 있는 경우 응용 프로그램을 조작하여 악성 코드를 실행하게 하고 시스템을 제어할 수 있습니다.dl()
을 사용하여 sockets.dll
이라는 이름의 라이브러리에서 코드를 로드하며, 이는 설치나 구성에 따라 다양한 위치에서 로드될 수 있습니다.
...
dl("sockets");
...
dl()
가 로드할 라이브러리에 대해 경로가 아닌 라이브러리 이름을 받는다는 점입니다.sockets.dll
의 악성 복사본을 배치하게 되면 응용 프로그램은 의도한 파일이 아닌 악성 복사본을 로드합니다. 응용 프로그램은 그 속성 때문에 높은 권한으로 실행됩니다. 즉, 공격자의 sockets.dll
의 내용이 높은 권한으로 실행되어 공격자에게 시스템의 완전한 제어권을 넘겨줄 수 있습니다.Kernel.system()
을 실행하여 보통 표준 시스템 디렉터리에 있는 program.exe
라는 실행 파일을 실행합니다.
...
system("program.exe")
...
Kernel.system()
은 셸을 통해 무언가를 실행합니다. 공격자가 환경 변수 RUBYSHELL
또는 COMSPEC
을 조작할 수 있다면 Kernel.system()
에 지정되는 명령으로 호출되는 악성 실행 파일을 가리킬 수 있습니다. 응용 프로그램은 그 속성 때문에 시스템 작업을 수행하는 데 필요한 권한으로 실행됩니다. 즉, 공격자의 program.exe
는 이 권한으로 실행되어 공격자에게 시스템의 완전한 제어권을 넘겨줄 수 있습니다.Kernel.system()
호출을 실행하기 전에 환경이 정리되지 않는다는 점입니다. 공격자가 $PATH
변수를 수정하여 program.exe
라는 악성 이진 파일을 가리키도록 하고 자신의 환경에서 프로그램을 실행하면 원하는 파일 대신 악성 이진 파일이 로드됩니다. 응용 프로그램은 그 속성 때문에 시스템 작업을 수행하는 데 필요한 권한으로 실행됩니다. 즉, 공격자의 program.exe
는 이 권한으로 실행되어 공격자에게 시스템의 완전한 제어권을 넘겨줄 수 있습니다.Windows.Storage.ApplicationData
클래스의 RoamingFolder
또는 RoamingSettings
속성을 사용하고 있습니다.RoamingFolder
및 RoamingSettings
속성은 로밍 응용 프로그램 데이터 저장소에서 컨테이너를 가져옵니다. 이 컨테이너는 2개 이상의 장치에서 데이터를 공유하는 데 사용할 수 있습니다. 개발자는 로밍 앱 데이터 저장소에 저장된 개체를 쓰고 읽음으로써 손상될 위험을 높입니다. 여기에는 로밍 앱 데이터 저장소를 통해 해당 개체를 공유하는 데이터, 응용 프로그램 및 시스템의 기밀성, 무결성 및 가용성이 포함됩니다.null
인지 검사하기 전에 null
이 될 수 있는 포인터를 역참조할 경우 발생합니다. 검사 후 역참조(dereference-after-check) 오류는 프로그램이 null
인지 여부를 명시적으로 검사하지만, null
인 것으로 알려진 포인터를 역참조할 때 발생합니다. 이 유형의 오류는 철자 오류 또는 프로그래머의 실수로 발생합니다. 저장 후 역참조(dereference-after-store) 오류는 프로그램이 명시적으로 포인터를 null
로 설정하고 이를 나중에 역참조할 경우에 발생합니다. 이 오류는 프로그래머가 선언된 상태의 변수를 null
로 초기화하여 발생하는 경우가 많습니다.foo
가 null
임을 확인한 다음 잘못 역참조합니다. if
문에서 검사할 때 foo
가 null
이면 null
dereference가 발생하여 null 포인터 예외가 일어납니다.예제 2: 다음 코드에서 프로그래머는 변수
if (foo is null) {
foo.SetBar(val);
...
}
foo
가 null
이 아닌 것으로 가정한 다음 개체를 역참조하여 이 가정을 확인합니다. 그러나 프로그래머는 나중에 foo
를 null
에 대해 검사함으로써 가정을 반박합니다. if
문에서 검사할 때 foo
가 null
이 되면 역참조될 때도 null
이 되어 null 포인터 예외가 발생할 수 있습니다. 역참조가 안전하지 않거나 이후의 검사가 필요하지 않게 됩니다.예제 3: 다음 코드에서 프로그래머는 명시적으로 변수
foo.SetBar(val);
...
if (foo is not null) {
...
}
foo
를 null
로 설정합니다. 나중에 프로그래머는 개체의 null
값을 검사하기 전에 foo
를 역참조합니다.
Foo foo = null;
...
foo.SetBar(val);
...
}
null
인지 검사하기 전에 null
이 될 수 있는 포인터를 역참조할 경우 발생합니다. 검사 후 역참조(dereference-after-check) 오류는 프로그램이 null
인지 여부를 명시적으로 검사하지만, null
인 것으로 알려진 포인터를 역참조할 때 발생합니다. 이 유형의 오류는 철자 오류 또는 프로그래머의 실수로 발생합니다. 저장 후 역참조(dereference-after-store) 오류는 프로그램이 명시적으로 포인터를 null
로 설정하고 이를 나중에 역참조할 경우에 발생합니다. 이 오류는 프로그래머가 선언된 상태의 변수를 null
로 초기화하여 발생하는 경우가 많습니다.ptr
이 NULL
이 아닌 것으로 가정합니다. 이 가정은 프로그래머가 포인터를 역참조하면 명시적인 것이 됩니다. 프로그래머가 NULL
에 대해 ptr
을 검사하면 이 가정이 반박됩니다. if
문에서 검사할 때 ptr
이 NULL
이 되면 역참조될 때도 NULL
이 되어 조각화 오류가 발생할 수 있습니다.예제 2: 다음 코드에서 프로그래머는 변수
ptr->field = val;
...
if (ptr != NULL) {
...
}
ptr
이 NULL
임을 확인한 다음 실수로 역참조합니다. if
문에서 검사할 때 ptr
이 NULL
이면 null
dereference가 발생하여 조각화 오류가 일어납니다.예제 3: 다음 코드에서 프로그래머는
if (ptr == null) {
ptr->field = val;
...
}
'\0'
문자열이 실제로 0 또는 NULL
인 것을 잊고 null 포인터를 역참조하여 조각화 오류가 일어납니다.예제 4: 다음 코드에서 프로그래머는 명시적으로 변수
if (ptr == '\0') {
*ptr = val;
...
}
ptr
를 NULL
로 설정합니다. 나중에 프로그래머는 개체의 null
값을 검사하기 전에 ptr
를 역참조합니다.
*ptr = NULL;
...
ptr->field = val;
...
}
null
인지 여부를 명시적으로 검사하지만, null
인 것으로 알려진 개체를 역참조할 때 발생합니다. 이 유형의 오류는 철자 오류 또는 프로그래머의 실수로 발생합니다.foo
이 null
임을 확인한 다음 실수로 역참조합니다. if
문에서 검사할 때 foo
가 null
이면 null
dereference가 발생하여 null 포인터 예외가 일어납니다.
if (foo == null) {
foo.setBar(val);
...
}