response.sendRedirect("j_security_check?j_username="+usr+"&j_password="+pass);
...
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로 인코딩된 값임을 쉽게 알 수 있습니다. 비양심적인 직원이 이 정보에 대한 액세스 권한을 갖게 되면 이를 사용하여 시스템에 침입할 수 있습니다.
String password=request.getParameter("password");
...
DefaultUser user = (DefaultUser) ESAPI.authenticator().createUser(username, password, password);
...
*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
...
...
" 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
파일을 덮어씁니다. 이로 인해 공격자는 코드 실행을 야기할 수 있는 악성 코드를 삽입할 수 있게 됩니다.
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
작업을 통해 인텐트를 수신 대기하도록 등록된 모든 수신자가 메시지를 수신합니다. 이 경우 수정 방법으로 권한을 사용하지 않는 것이 좋지만, 브로드캐스트는 수신자 수를 제한하는 권한으로도 보호되지 않습니다.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
에서 파생된 예외 사항도 처리하는데 이는 프로그래머의 의도와 반대되는 것입니다.Exception
또는 Throwable
이 발생하도록 선언하면 호출자가 올바른 오류 처리 및 오류 복구 작업을 수행하기 어렵습니다. Java의 예외 메커니즘은 호출자가 손쉽게 잘못을 예상하고 각각의 고유한 예외 상황을 처리하기 위한 코드를 쓸 수 있도록 설계되어 있습니다. 일반적인 형태의 예외 사항이 발생하도록 메서드를 선언하면 이런 메커니즘이 아무 소용이 없습니다.
public void doExchange()
throws IOException, InvocationTargetException,
SQLException {
...
}
public void doExchange()
throws Exception {
...
}
doExchange()
의 이후 수정 버전에서 이전 예외 사항과 다르게 처리해야 하는 새 형식의 예외 사항을 도입하는 경우 이 요구 사항을 쉽게 적용할 수 없습니다.NullPointerException
을 캐치(catch)하는 것은 일반적으로 잘못된 관행입니다.NullPointerException
을 캐치(catch)합니다.NullPointerException
을 발생시켜 오류 조건을 알립니다.NullPointerException
을 캐치하는 오류를 범합니다.
try {
mysteryMethod();
}
catch (NullPointerException npe) {
}
finally
블록 내부에서 반환하면 예외 사항이 손실될 수 있습니다.finally
블록에 return 문이 있으면 try 블록에서 발생하는 예외 사항이 사라질 수 있습니다.true
와 함께 두 번째 doMagic
호출에서 발생한 MagicException
은 호출자에게 전달되지 않습니다. finally
블록에 return 문이 있으면 예외 사항이 사라집니다.
public class MagicTrick {
public static class MagicException extends Exception { }
public static void main(String[] args) {
System.out.println("Watch as this magical code makes an " +
"exception disappear before your very eyes!");
System.out.println("First, the kind of exception handling " +
"you're used to:");
try {
doMagic(false);
} catch (MagicException e) {
// An exception will be caught here
e.printStackTrace();
}
System.out.println("Now, the magic:");
try {
doMagic(true);
} catch (MagicException e) {
// No exception caught here, the finally block ate it
e.printStackTrace();
}
System.out.println("tada!");
}
public static void doMagic(boolean returnFromFinally)
throws MagicException {
try {
throw new MagicException();
}
finally {
if (returnFromFinally) {
return;
}
}
}
}
finally
블록 내부에서 반환하면 예외 사항이 손실될 수 있습니다.finally
블록에 return 문이 있으면 try 블록에서 발생하는 예외 사항이 사라질 수 있습니다.True
와 함께 두 번째 doMagic
호출에서 발생한 exception
은 호출자에게 전달되지 않습니다. finally
블록에 return 문이 있으면 예외 사항이 사라집니다."disappear before your very eyes!" . PHP_EOL;
echo "First, the kind of exception handling " .
"you're used to:" . PHP_EOL;
try {
doMagic(False);
} catch (exception $e) {
// An exception will be caught here
echo $e->getMessage();
}
echo "Now, the magic:" . PHP_EOL;
try {
doMagic(True);
} catch (exception $e) {
// No exception caught here, the finally block ate it
echo $e->getMessage();
}
echo "Tada!" . PHP_EOL;
function doMagic($returnFromFinally) {
try {
throw new Exception("Magic Exception" . PHP_EOL);
}
finally {
if ($returnFromFinally) {
return;
}
}
}
?>
ThreadDeath
오류가 다시 발생하지 않는 경우, 문제의 스레드는 실제로 정지된 것이 아닐 수도 있습니다.ThreadDeath
오류는 응용 프로그램을 비동기적으로 종료한 후 정리해야 할 경우에만 발생되어야 합니다. ThreadDeath
오류가 발생한 경우, 스레드가 실제로 정지하도록 다시 발생시키는 것이 중요합니다. ThreadDeath
발생의 목적은 스레드를 중단시키는 것입니다. ThreadDeath
를 덮어버리면 스레드 중단을 막을 수 있고 원래 ThreadDeath
를 발생시킨 사람은 누구라도 스레드 중단을 기대하므로 예기치 못한 동작이 발생할 수 있습니다.ThreadDeath
오류를 찾지만 다시 발생시키지 않습니다.
try
{
//some code
}
catch(ThreadDeath td)
{
//clean up code
}
finally
블록 내에서 throw
문을 사용하면 try-catch-finally
를 통한 논리 진행이 깨집니다.finally
블록은 항상 해당 try-catch
블록 뒤에서 실행되며 대개 파일 핸들 또는 데이터베이스 커서와 같이 할당된 리소스를 해제하는 데 사용됩니다. finally
블록에서 예외를 발생시키면 정상적인 프로그램 실행이 손상되므로 중요한 정리 코드를 무시할 수 있습니다. stmt.close()
에 대한 호출은 FileNotFoundException
이 발생할 때 무시됩니다.
public void processTransaction(Connection conn) throws FileNotFoundException
{
FileInputStream fis = null;
Statement stmt = null;
try
{
stmt = conn.createStatement();
fis = new FileInputStream("badFile.txt");
...
}
catch (FileNotFoundException fe)
{
log("File not found.");
}
catch (SQLException se)
{
//handle error
}
finally
{
if (fis == null)
{
throw new FileNotFoundException();
}
if (stmt != null)
{
try
{
stmt.close();
}
catch (SQLException e)
{
log(e);
}
}
}
}
javax.net.ssl.SSLHandshakeException
, javax.net.ssl.SSLKeyException
및 javax.net.ssl.SSLPeerUnverifiedException
모두 SSL 연결과 관련된 중요한 오류를 전달합니다. 이러한 오류가 올바로 처리되지 않는 경우, 이 연결은 예상치 못한 상태 및 잠재적 불안정 상태가 될 수 있습니다.
private final Logger logger =
Logger.getLogger(MyClass.class);
public class MyClass {
private final static Logger good =
Logger.getLogger(MyClass.class);
private final static Logger bad =
Logger.getLogger(MyClass.class);
private final static Logger ugly =
Logger.getLogger(MyClass.class);
...
}
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")
...
public class Totaller {
private int total;
public int total() {
...
}
}
synchronized(this) { }
finalize()
메서드는 개체를 가비지 수집(garbage collection)한 후 JVM 만 호출할 수 있습니다.finalize()
메서드를 종결자(finalizer) 외부에서 호출할 수 있다고 되어 있지만 이는 일반적으로 잘못된 방법입니다. 예를 들어, finalize()
를 명시적으로 호출하면 finalize()
가 두 번 이상 호출됩니다. 첫 호출은 명시적인 호출이고 마지막 호출은 개체를 가비지 수집(garbage collection)한 후 이루어지는 호출입니다.finalize()
를 명시적으로 호출합니다.
// time to clean up
widget.finalize();