오류 및 오류 처리는 API 클래스를 나타냅니다. 오류 처리와 관련된 오류는 매우 흔하므로 따로 다룰 만한 내용입니다. "API 오용"과 마찬가지로 오류 관련 보안 취약성을 일으키는 두 가지 원인이 있습니다. 가장 흔한 것은 오류를 제대로(혹은 아예) 처리하지 못하는 것입니다. 두 번째는 (잠재적 공격자에게) 너무 많은 정보를 제공하거나 처리하기 어려운 오류를 발생시키는 것입니다.
...
EXEC CICS
INGNORE CONDITION ERROR
END-EXEC.
...
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
이 발생해도 프로그램은 아무 일도 없었던 것처럼 계속 실행됩니다. 프로그램은 특수한 상황을 나타내는 증거를 전혀 기록하지 않기 때문에 이후에 프로그램의 동작을 밝히려는 노력이 실패할 수 있습니다.
PROCEDURE do_it_all
IS
BEGIN
BEGIN
INSERT INTO table1 VALUES(...);
COMMIT;
EXCEPTION
WHEN OTHERS THEN NULL;
END;
END do_it_all;
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
을 캐치(catch)하는 오류를 범합니다.
try {
mysteryMethod();
}
catch (NullPointerException npe) {
}
NullReferenceException
을 캐치(catch)하는 것은 일반적으로 잘못된 관행입니다.NullReferenceException
을 캐치(catch)합니다. NullReferenceException
을 발생시켜 오류 조건을 알립니다.NullReferenceException
을 잘못 catch합니다.
try {
MysteryMethod();
}
catch (NullReferenceException 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 연결과 관련된 중요한 오류를 전달합니다. 이러한 오류가 올바로 처리되지 않는 경우, 이 연결은 예상치 못한 상태 및 잠재적 불안정 상태가 될 수 있습니다.