"이중 해싱"은 비밀번호를 한 번만 해시하는 것보다 덜 안전합니까?


293

저장하기 전에 비밀번호를 두 번 해싱하는 것이 비밀번호를 한 번 해싱하는 것보다 다소 안전합니까?

내가 말하는 것은 이것을하는 것입니다.

$hashed_password = hash(hash($plaintext_password));

이 대신에 :

$hashed_password = hash($plaintext_password);

덜 안전하다면 좋은 설명 (또는 그에 대한 링크)을 제공 할 수 있습니까?

또한 사용 된 해시 함수가 차이를 만들어 내나요? 동일한 해시 함수를 반복하는 대신 md5와 sha1 (예를 들어)을 혼합하면 차이가 있습니까?

참고 1 : "이중 해싱"이라고 말하면 암호를 더 모호하게 만들기 위해 암호를 두 번 해싱하는 것에 대해 이야기하고 있습니다. 충돌을 해결 하는 기술에 대해서는 이야기하지 않습니다 .

참고 2 : 실제로 소금을 만들기 위해 무작위 소금을 추가해야한다는 것을 알고 있습니다. 문제는 동일한 알고리즘으로 두 번 해싱하는 것이 해시에 도움이되는지 아프게하는 것입니다.


2
Hash(password)그리고 Hash(Hash(password))똑같이 불안. 둘 다 시맨틱 보안 이라는 개념이 결여되어있다 . 즉, 출력 무작위와 구별 가능합니다. 예를 들어 MD5("password")입니다 5f4dcc3b5aa765d61d8327deb882cf99. 그게의 MD5 해시 알고 password, 그것은 이다 무작위로 구별. 대신 HMAC를 사용해야합니다. 확실하게 안전하고 PRF입니다.
jww

답변:


267

비밀번호를 한 번 해싱하는 것은 안전하지 않습니다

아니요, 다중 해시는 덜 안전하지 않습니다. 보안 암호 사용의 필수 부분입니다.

해시를 반복하면 공격자가 후보 목록에서 각 암호를 시도하는 데 걸리는 시간이 늘어납니다. 암호를 공격하는 데 걸리는 시간을 몇 시간에서 몇 년으로 쉽게 늘릴 수 있습니다.

간단한 반복으로는 충분하지 않습니다

해시 출력을 입력에 연결하는 것만으로는 보안이 충분하지 않습니다. 암호의 엔트로피를 유지하는 알고리즘의 컨텍스트에서 반복이 이루어져야합니다. 운 좋게도, 디자인에 대한 확신을주기 위해 충분히 조사 된 알고리즘이 여러 개 있습니다.

PBKDF2와 같은 우수한 키 파생 알고리즘은 각 해싱 라운드에 암호를 주입하여 해시 출력의 충돌에 대한 우려를 완화합니다. PBKDF2는 그대로 비밀번호 인증에 사용될 수 있습니다. Bcrypt는 키 단계를 따라 암호화 단계를 따릅니다. 이렇게하면 키 파생을 되 돌리는 빠른 방법이 발견되면 공격자는 여전히 알려진 일반 텍스트 공격을 완료해야합니다.

비밀번호를 끊는 방법

저장된 비밀번호는 오프라인 공격으로부터 보호해야합니다. 암호가 솔트되지 않으면 사전 계산 된 사전 공격 (예 : Rainbow Table 사용)으로 암호가 손상 될 수 있습니다. 그렇지 않으면 공격자는 각 암호의 해시를 계산하고 저장된 해시와 일치하는지 확인하는 데 시간을 소비해야합니다.

모든 비밀번호가 동일하지는 않습니다. 공격자는 모든 짧은 암호를 철저히 검색 할 수 있지만, 추가 캐릭터마다 무차별 대입 성공 확률이 급격히 떨어질 것임을 알고 있습니다. 대신, 가장 가능성이 높은 암호의 순서가 지정된 목록을 사용합니다. "password123"으로 시작하여 자주 사용하지 않는 암호로 진행합니다.

공격자 목록이 길고 100 억 명의 후보자가 있다고 가정하겠습니다. 데스크탑 시스템이 초당 백만 해시를 계산할 수 있다고 가정하십시오. 한 번의 반복 만 사용하는 경우 공격자는 전체 목록이 3 시간 미만인지 테스트 할 수 있습니다. 그러나 단지 2000 번의 반복이 사용된다면, 그 시간은 거의 8 개월로 연장됩니다. 예를 들어 GPU의 성능을 활용할 수있는 프로그램을 다운로드 할 수있는보다 정교한 공격자를 물 리치려면 더 많은 반복이 필요합니다.

얼마입니까?

반복 횟수는 보안과 사용자 경험 사이의 균형입니다. 공격자가 사용할 수있는 특수화 된 하드웨어는 저렴하지만 초당 수억 번의 반복 작업을 수행 할 수 있습니다. 공격자의 시스템 성능에 따라 여러 번 반복하여 암호를 해독하는 데 걸리는 시간이 결정됩니다. 그러나 응용 프로그램은이 특수 하드웨어를 사용하지 않을 것입니다. 얼마나 많은 반복을 수행 할 수있는 악화 사용자가없는가에 따라 당신의 시스템.

인증하는 동안 사용자가 추가로 3 분 정도 더 기다리도록 할 수 있습니다. 대상 플랫폼을 프로파일 링하고 가능한 한 많은 반복을 사용하십시오. 내가 테스트 한 플랫폼 (모바일 장치의 한 사용자 또는 서버 플랫폼의 많은 사용자)은 60,000 ~ 120,000 반복의 PBKDF2 를 편안하게 지원 하거나 비용 요소 12 또는 13의 bcrypt 를 편안하게 지원할 수 있습니다 .

더 많은 배경

해싱에서 솔트와 반복의 역할에 대한 권위있는 정보는 PKCS # 5를 읽으십시오. PBKDF2는 비밀번호에서 암호화 키를 생성하기위한 것이지만 비밀번호 인증을위한 단방향 해시로 작동합니다. 각 bcrypt 반복은 SHA-2 해시보다 비싸므로 반복을 적게 사용할 수 있지만 아이디어는 동일합니다. 또한 Bcrypt는 파생 된 키를 사용하여 잘 알려진 일반 텍스트를 암호화함으로써 대부분의 PBKDF2 기반 솔루션보다 한 발 앞서 있습니다. 결과 암호 텍스트는 일부 메타 데이터와 함께 "해시"로 저장됩니다. 그러나 PBKDF2와 동일한 작업을 수행하는 데 방해가되는 것은 없습니다.

이 주제에 대해 쓴 다른 답변은 다음과 같습니다.


68
손상된 인증 저장소에 대한 사전 공격을 방지하려는 경우 의도적으로 느린 알고리즘을 만드는 것이 허용되는 방법입니다. 이 기술을 "키 강화"또는 "키 스트레칭"이라고합니다. 참조 en.wikipedia.org/wiki/Key_stretching

17
@RoBorg : 그것은 어떻게 속도를 중요하지 않습니다 당신의 구현이지만, 어떻게 공격자의 구현이 될 것입니다 둔화 : 해시 자체가 느린 수천 번 경우, 그것은 긴 암호를 무차별로 배의 공격자 수천 소요됩니다.
orip December

5
아마도 128 비트 공간 0에서 2 ^ 128-1 사이의 충돌을 원할 것입니다. 해시 알고리즘의 2 ^ 128 출력 공간이 완벽하다면 이론적으로 2 ^ 128 글리프의 알파벳으로 대체 암호가 있습니다.
jmucchiello

13
@devin- "나의 솔루션"이 아니며, PKCS # 5와 같은 암호 기반 암호화 표준에 내장되어 있으며 Robert Morris와 같은 전문가가 권장하는 널리 사용되는 관행입니다. 합법적 인 응용 프로그램에서는 사용자 인증에 소요되는 시간이 적기 때문에 확장 성이 매우 뛰어납니다. 응용 프로그램이 암호를 해독 할 때만 확장이 어려워 지므로 권장 사항입니다. 확실히, 해시의 검색 공간은 가능한 암호의 검색 공간보다 작지만 128 비트 공간조차도 무차별 검색을하기에는 너무 큽니다. 방어 할 위협은 오프라인 사전 공격입니다.
erickson

6
개별 사용자에게는 불편 함을 말하는 것이 아니라 사용자 수가 많을 경우 서버에 가해지는 스트레스는 CPU 부하에 의존하여 요청 횟수를 늦추기 때문에 서버에 가해지는 스트레스를 말합니다. 즉, 더 많은 CPU 성능을 추가하면 이러한 무차별 대입 공격자에 대한 제한이 줄어 듭니다. -그러나 확장 성과 널리 사용되는 방법에 대해서는 완전히 정확합니다. 나는 이전에 언급 한 거의 모든 내용에 대해 틀렸다. 죄송합니다 :)
DevinB

227

안전하다고 말하는 사람들에게는 일반적으로 정확합니다 . "이중"해싱 (또는 해시 함수를 반복하는 논리적 확장)은 특정 문제에 대해 올바르게 수행되면 절대적으로 안전 합니다 .

안전하지 않다고 말하는 사람들에게는 이 경우에 맞습니다 . 질문에 게시 된 코드 안전하지 않습니다. 이유에 대해 이야기 해 봅시다 :

$hashed_password1 = md5( md5( plaintext_password ) );
$hashed_password2 = md5( plaintext_password );

우리가 우려하는 해시 함수에는 두 가지 기본 속성이 있습니다.

  1. 사전 이미지 저항 -해시가 주어지면 다음 과 같은 $h메시지를 찾기가 어렵습니다.$m$h === hash($m)

  2. 2 차 사전 이미지 저항 -메시지가 주어지면 다음 과 같은 $m1다른 메시지를 찾기가 어렵습니다.$m2hash($m1) === hash($m2)

  3. 충돌 방지 -다음 ($m1, $m2)과 같은 메시지 쌍을 찾기가 어려워 야합니다 hash($m1) === hash($m2)(이것은 2 차 사전 이미지 저항과 유사하지만 공격자가 두 메시지를 제어 할 수 있다는 점에서 다릅니다) ...

암호 저장을 위해, 우리가 정말로 염려하는 것은 사전 이미지 저항 입니다. 다른 두 개는 $m1우리가 안전하게 유지하려고하는 사용자의 비밀번호 이기 때문에 무질서 합니다. 따라서 공격자가 이미 가지고 있다면 해시는 보호 할 것이 없습니다.

기권

다음에 오는 모든 것은 우리가 관심있는 모든 것이 사전 이미지 저항 이라는 전제를 기반으로합니다 . 해시 함수의 다른 두 가지 기본 속성은 같은 방식으로 유지되지 않을 수 있습니다. 따라서이 게시물의 결론 은 암호 저장에 해시 기능을 사용할 때만 적용됩니다. 그들은 일반적으로 적용되지 않습니다 ...

시작하자

이 논의를 위해 자체 해시 함수를 발명 해 보겠습니다.

function ourHash($input) {
    $result = 0;
    for ($i = 0; $i < strlen($input); $i++) {
        $result += ord($input[$i]);
    }
    return (string) ($result % 256);
}

이제이 해시 함수의 기능이 매우 분명합니다. 각 입력 문자의 ASCII 값을 합한 다음 해당 결과의 모듈로를 256으로 취합니다.

테스트 해보자.

var_dump(
    ourHash('abc'), // string(2) "38"
    ourHash('def'), // string(2) "47"
    ourHash('hij'), // string(2) "59"
    ourHash('klm')  // string(2) "68"
);

이제 함수 주위에서 몇 번 실행하면 어떻게되는지 봅시다 :

$tests = array(
    "abc",
    "def",
    "hij",
    "klm",
);

foreach ($tests as $test) {
    $hash = $test;
    for ($i = 0; $i < 100; $i++) {
        $hash = ourHash($hash);
    }
    echo "Hashing $test => $hash\n";
}

출력 :

Hashing abc => 152
Hashing def => 152
Hashing hij => 155
Hashing klm => 155

와우 충돌이 발생했습니다 !!! 왜 그런지 살펴 보자.

다음은 가능한 모든 해시 출력의 문자열을 해싱 한 결과입니다.

Hashing 0 => 48
Hashing 1 => 49
Hashing 2 => 50
Hashing 3 => 51
Hashing 4 => 52
Hashing 5 => 53
Hashing 6 => 54
Hashing 7 => 55
Hashing 8 => 56
Hashing 9 => 57
Hashing 10 => 97
Hashing 11 => 98
Hashing 12 => 99
Hashing 13 => 100
Hashing 14 => 101
Hashing 15 => 102
Hashing 16 => 103
Hashing 17 => 104
Hashing 18 => 105
Hashing 19 => 106
Hashing 20 => 98
Hashing 21 => 99
Hashing 22 => 100
Hashing 23 => 101
Hashing 24 => 102
Hashing 25 => 103
Hashing 26 => 104
Hashing 27 => 105
Hashing 28 => 106
Hashing 29 => 107
Hashing 30 => 99
Hashing 31 => 100
Hashing 32 => 101
Hashing 33 => 102
Hashing 34 => 103
Hashing 35 => 104
Hashing 36 => 105
Hashing 37 => 106
Hashing 38 => 107
Hashing 39 => 108
Hashing 40 => 100
Hashing 41 => 101
Hashing 42 => 102
Hashing 43 => 103
Hashing 44 => 104
Hashing 45 => 105
Hashing 46 => 106
Hashing 47 => 107
Hashing 48 => 108
Hashing 49 => 109
Hashing 50 => 101
Hashing 51 => 102
Hashing 52 => 103
Hashing 53 => 104
Hashing 54 => 105
Hashing 55 => 106
Hashing 56 => 107
Hashing 57 => 108
Hashing 58 => 109
Hashing 59 => 110
Hashing 60 => 102
Hashing 61 => 103
Hashing 62 => 104
Hashing 63 => 105
Hashing 64 => 106
Hashing 65 => 107
Hashing 66 => 108
Hashing 67 => 109
Hashing 68 => 110
Hashing 69 => 111
Hashing 70 => 103
Hashing 71 => 104
Hashing 72 => 105
Hashing 73 => 106
Hashing 74 => 107
Hashing 75 => 108
Hashing 76 => 109
Hashing 77 => 110
Hashing 78 => 111
Hashing 79 => 112
Hashing 80 => 104
Hashing 81 => 105
Hashing 82 => 106
Hashing 83 => 107
Hashing 84 => 108
Hashing 85 => 109
Hashing 86 => 110
Hashing 87 => 111
Hashing 88 => 112
Hashing 89 => 113
Hashing 90 => 105
Hashing 91 => 106
Hashing 92 => 107
Hashing 93 => 108
Hashing 94 => 109
Hashing 95 => 110
Hashing 96 => 111
Hashing 97 => 112
Hashing 98 => 113
Hashing 99 => 114
Hashing 100 => 145
Hashing 101 => 146
Hashing 102 => 147
Hashing 103 => 148
Hashing 104 => 149
Hashing 105 => 150
Hashing 106 => 151
Hashing 107 => 152
Hashing 108 => 153
Hashing 109 => 154
Hashing 110 => 146
Hashing 111 => 147
Hashing 112 => 148
Hashing 113 => 149
Hashing 114 => 150
Hashing 115 => 151
Hashing 116 => 152
Hashing 117 => 153
Hashing 118 => 154
Hashing 119 => 155
Hashing 120 => 147
Hashing 121 => 148
Hashing 122 => 149
Hashing 123 => 150
Hashing 124 => 151
Hashing 125 => 152
Hashing 126 => 153
Hashing 127 => 154
Hashing 128 => 155
Hashing 129 => 156
Hashing 130 => 148
Hashing 131 => 149
Hashing 132 => 150
Hashing 133 => 151
Hashing 134 => 152
Hashing 135 => 153
Hashing 136 => 154
Hashing 137 => 155
Hashing 138 => 156
Hashing 139 => 157
Hashing 140 => 149
Hashing 141 => 150
Hashing 142 => 151
Hashing 143 => 152
Hashing 144 => 153
Hashing 145 => 154
Hashing 146 => 155
Hashing 147 => 156
Hashing 148 => 157
Hashing 149 => 158
Hashing 150 => 150
Hashing 151 => 151
Hashing 152 => 152
Hashing 153 => 153
Hashing 154 => 154
Hashing 155 => 155
Hashing 156 => 156
Hashing 157 => 157
Hashing 158 => 158
Hashing 159 => 159
Hashing 160 => 151
Hashing 161 => 152
Hashing 162 => 153
Hashing 163 => 154
Hashing 164 => 155
Hashing 165 => 156
Hashing 166 => 157
Hashing 167 => 158
Hashing 168 => 159
Hashing 169 => 160
Hashing 170 => 152
Hashing 171 => 153
Hashing 172 => 154
Hashing 173 => 155
Hashing 174 => 156
Hashing 175 => 157
Hashing 176 => 158
Hashing 177 => 159
Hashing 178 => 160
Hashing 179 => 161
Hashing 180 => 153
Hashing 181 => 154
Hashing 182 => 155
Hashing 183 => 156
Hashing 184 => 157
Hashing 185 => 158
Hashing 186 => 159
Hashing 187 => 160
Hashing 188 => 161
Hashing 189 => 162
Hashing 190 => 154
Hashing 191 => 155
Hashing 192 => 156
Hashing 193 => 157
Hashing 194 => 158
Hashing 195 => 159
Hashing 196 => 160
Hashing 197 => 161
Hashing 198 => 162
Hashing 199 => 163
Hashing 200 => 146
Hashing 201 => 147
Hashing 202 => 148
Hashing 203 => 149
Hashing 204 => 150
Hashing 205 => 151
Hashing 206 => 152
Hashing 207 => 153
Hashing 208 => 154
Hashing 209 => 155
Hashing 210 => 147
Hashing 211 => 148
Hashing 212 => 149
Hashing 213 => 150
Hashing 214 => 151
Hashing 215 => 152
Hashing 216 => 153
Hashing 217 => 154
Hashing 218 => 155
Hashing 219 => 156
Hashing 220 => 148
Hashing 221 => 149
Hashing 222 => 150
Hashing 223 => 151
Hashing 224 => 152
Hashing 225 => 153
Hashing 226 => 154
Hashing 227 => 155
Hashing 228 => 156
Hashing 229 => 157
Hashing 230 => 149
Hashing 231 => 150
Hashing 232 => 151
Hashing 233 => 152
Hashing 234 => 153
Hashing 235 => 154
Hashing 236 => 155
Hashing 237 => 156
Hashing 238 => 157
Hashing 239 => 158
Hashing 240 => 150
Hashing 241 => 151
Hashing 242 => 152
Hashing 243 => 153
Hashing 244 => 154
Hashing 245 => 155
Hashing 246 => 156
Hashing 247 => 157
Hashing 248 => 158
Hashing 249 => 159
Hashing 250 => 151
Hashing 251 => 152
Hashing 252 => 153
Hashing 253 => 154
Hashing 254 => 155
Hashing 255 => 156

숫자가 높아지는 경향에 주목하십시오. 그것은 우리의 몰락으로 밝혀졌습니다. 해시를 4 번 실행하면 (각 요소에 대해 $ hash = ourHash ($ hash)`) 다음과 같이 나타납니다.

Hashing 0 => 153
Hashing 1 => 154
Hashing 2 => 155
Hashing 3 => 156
Hashing 4 => 157
Hashing 5 => 158
Hashing 6 => 150
Hashing 7 => 151
Hashing 8 => 152
Hashing 9 => 153
Hashing 10 => 157
Hashing 11 => 158
Hashing 12 => 150
Hashing 13 => 154
Hashing 14 => 155
Hashing 15 => 156
Hashing 16 => 157
Hashing 17 => 158
Hashing 18 => 150
Hashing 19 => 151
Hashing 20 => 158
Hashing 21 => 150
Hashing 22 => 154
Hashing 23 => 155
Hashing 24 => 156
Hashing 25 => 157
Hashing 26 => 158
Hashing 27 => 150
Hashing 28 => 151
Hashing 29 => 152
Hashing 30 => 150
Hashing 31 => 154
Hashing 32 => 155
Hashing 33 => 156
Hashing 34 => 157
Hashing 35 => 158
Hashing 36 => 150
Hashing 37 => 151
Hashing 38 => 152
Hashing 39 => 153
Hashing 40 => 154
Hashing 41 => 155
Hashing 42 => 156
Hashing 43 => 157
Hashing 44 => 158
Hashing 45 => 150
Hashing 46 => 151
Hashing 47 => 152
Hashing 48 => 153
Hashing 49 => 154
Hashing 50 => 155
Hashing 51 => 156
Hashing 52 => 157
Hashing 53 => 158
Hashing 54 => 150
Hashing 55 => 151
Hashing 56 => 152
Hashing 57 => 153
Hashing 58 => 154
Hashing 59 => 155
Hashing 60 => 156
Hashing 61 => 157
Hashing 62 => 158
Hashing 63 => 150
Hashing 64 => 151
Hashing 65 => 152
Hashing 66 => 153
Hashing 67 => 154
Hashing 68 => 155
Hashing 69 => 156
Hashing 70 => 157
Hashing 71 => 158
Hashing 72 => 150
Hashing 73 => 151
Hashing 74 => 152
Hashing 75 => 153
Hashing 76 => 154
Hashing 77 => 155
Hashing 78 => 156
Hashing 79 => 157
Hashing 80 => 158
Hashing 81 => 150
Hashing 82 => 151
Hashing 83 => 152
Hashing 84 => 153
Hashing 85 => 154
Hashing 86 => 155
Hashing 87 => 156
Hashing 88 => 157
Hashing 89 => 158
Hashing 90 => 150
Hashing 91 => 151
Hashing 92 => 152
Hashing 93 => 153
Hashing 94 => 154
Hashing 95 => 155
Hashing 96 => 156
Hashing 97 => 157
Hashing 98 => 158
Hashing 99 => 150
Hashing 100 => 154
Hashing 101 => 155
Hashing 102 => 156
Hashing 103 => 157
Hashing 104 => 158
Hashing 105 => 150
Hashing 106 => 151
Hashing 107 => 152
Hashing 108 => 153
Hashing 109 => 154
Hashing 110 => 155
Hashing 111 => 156
Hashing 112 => 157
Hashing 113 => 158
Hashing 114 => 150
Hashing 115 => 151
Hashing 116 => 152
Hashing 117 => 153
Hashing 118 => 154
Hashing 119 => 155
Hashing 120 => 156
Hashing 121 => 157
Hashing 122 => 158
Hashing 123 => 150
Hashing 124 => 151
Hashing 125 => 152
Hashing 126 => 153
Hashing 127 => 154
Hashing 128 => 155
Hashing 129 => 156
Hashing 130 => 157
Hashing 131 => 158
Hashing 132 => 150
Hashing 133 => 151
Hashing 134 => 152
Hashing 135 => 153
Hashing 136 => 154
Hashing 137 => 155
Hashing 138 => 156
Hashing 139 => 157
Hashing 140 => 158
Hashing 141 => 150
Hashing 142 => 151
Hashing 143 => 152
Hashing 144 => 153
Hashing 145 => 154
Hashing 146 => 155
Hashing 147 => 156
Hashing 148 => 157
Hashing 149 => 158
Hashing 150 => 150
Hashing 151 => 151
Hashing 152 => 152
Hashing 153 => 153
Hashing 154 => 154
Hashing 155 => 155
Hashing 156 => 156
Hashing 157 => 157
Hashing 158 => 158
Hashing 159 => 159
Hashing 160 => 151
Hashing 161 => 152
Hashing 162 => 153
Hashing 163 => 154
Hashing 164 => 155
Hashing 165 => 156
Hashing 166 => 157
Hashing 167 => 158
Hashing 168 => 159
Hashing 169 => 151
Hashing 170 => 152
Hashing 171 => 153
Hashing 172 => 154
Hashing 173 => 155
Hashing 174 => 156
Hashing 175 => 157
Hashing 176 => 158
Hashing 177 => 159
Hashing 178 => 151
Hashing 179 => 152
Hashing 180 => 153
Hashing 181 => 154
Hashing 182 => 155
Hashing 183 => 156
Hashing 184 => 157
Hashing 185 => 158
Hashing 186 => 159
Hashing 187 => 151
Hashing 188 => 152
Hashing 189 => 153
Hashing 190 => 154
Hashing 191 => 155
Hashing 192 => 156
Hashing 193 => 157
Hashing 194 => 158
Hashing 195 => 159
Hashing 196 => 151
Hashing 197 => 152
Hashing 198 => 153
Hashing 199 => 154
Hashing 200 => 155
Hashing 201 => 156
Hashing 202 => 157
Hashing 203 => 158
Hashing 204 => 150
Hashing 205 => 151
Hashing 206 => 152
Hashing 207 => 153
Hashing 208 => 154
Hashing 209 => 155
Hashing 210 => 156
Hashing 211 => 157
Hashing 212 => 158
Hashing 213 => 150
Hashing 214 => 151
Hashing 215 => 152
Hashing 216 => 153
Hashing 217 => 154
Hashing 218 => 155
Hashing 219 => 156
Hashing 220 => 157
Hashing 221 => 158
Hashing 222 => 150
Hashing 223 => 151
Hashing 224 => 152
Hashing 225 => 153
Hashing 226 => 154
Hashing 227 => 155
Hashing 228 => 156
Hashing 229 => 157
Hashing 230 => 158
Hashing 231 => 150
Hashing 232 => 151
Hashing 233 => 152
Hashing 234 => 153
Hashing 235 => 154
Hashing 236 => 155
Hashing 237 => 156
Hashing 238 => 157
Hashing 239 => 158
Hashing 240 => 150
Hashing 241 => 151
Hashing 242 => 152
Hashing 243 => 153
Hashing 244 => 154
Hashing 245 => 155
Hashing 246 => 156
Hashing 247 => 157
Hashing 248 => 158
Hashing 249 => 159
Hashing 250 => 151
Hashing 251 => 152
Hashing 252 => 153
Hashing 253 => 154
Hashing 254 => 155
Hashing 255 => 156

우리는 8 개의 값으로 자신을 좁혔습니다 ... 나빴습니다 ... 원래의 함수는에 매핑 S(∞)되었습니다 S(256). 우리는에 대한 Surjective Function 매핑 $input을 만들었습니다 $output.

우리는 Surjective 함수를 가지고 있기 때문에 입력의 서브셋에 대한 매핑이 충돌하지 않을 것이라는 보장은 없습니다 (사실 실제로는).

그것이 여기서 일어난 일입니다! 우리의 기능은 나빴지 만 이것이 이것이 작동하는 이유는 아닙니다 (그래서 너무 빠르고 완벽하게 작동했습니다).

같은 일이 발생합니다 MD5. 이 매핑 S(∞)S(2^128). running MD5(S(output))Injective 일 것이라는 보장 이 없으므로 충돌이 발생하지 않습니다.

TL / DR 섹션

따라서 출력을 md5직접 피드백하면 충돌이 발생할 수 있으므로 반복 할 때마다 충돌 가능성이 높아집니다. 그러나 이것은 선형으로 증가하므로 결과 집합 2^128이 줄어드는 반면 중요한 결함이 될만큼 충분히 빨리 줄지 않습니다.

그래서,

$output = md5($input); // 2^128 possibilities
$output = md5($output); // < 2^128 possibilities
$output = md5($output); // < 2^128 possibilities
$output = md5($output); // < 2^128 possibilities
$output = md5($output); // < 2^128 possibilities

반복 횟수가 많을수록 축소가 더 진행됩니다.

수정

다행 스럽게도이 문제를 해결 하는 간단한 방법이 있습니다. 추가 반복 으로 무언가 를 피드백하십시오.

$output = md5($input); // 2^128 possibilities
$output = md5($input . $output); // 2^128 possibilities
$output = md5($input . $output); // 2^128 possibilities
$output = md5($input . $output); // 2^128 possibilities
$output = md5($input . $output); // 2^128 possibilities    

추가 반복은의 각 개별 값에 대해 2 ^ 128이 아닙니다 $input. 우리는 $input여전히 라인에 충돌 하는 값 을 생성 할 수 있음을 의미합니다 (따라서 2^128가능한 출력 보다 훨씬 적은 양으로 정착하거나 공명 합니다). 그러나 일반적인 경우 $input는 여전히 한 라운드만큼 강력합니다.

잠깐만 요? 우리의 ourHash()기능으로 이것을 시험해 봅시다 . $hash = ourHash($input . $hash);100 번 반복 하는 경우로 전환 :

Hashing 0 => 201
Hashing 1 => 212
Hashing 2 => 199
Hashing 3 => 201
Hashing 4 => 203
Hashing 5 => 205
Hashing 6 => 207
Hashing 7 => 209
Hashing 8 => 211
Hashing 9 => 204
Hashing 10 => 251
Hashing 11 => 147
Hashing 12 => 251
Hashing 13 => 148
Hashing 14 => 253
Hashing 15 => 0
Hashing 16 => 1
Hashing 17 => 2
Hashing 18 => 161
Hashing 19 => 163
Hashing 20 => 147
Hashing 21 => 251
Hashing 22 => 148
Hashing 23 => 253
Hashing 24 => 0
Hashing 25 => 1
Hashing 26 => 2
Hashing 27 => 161
Hashing 28 => 163
Hashing 29 => 8
Hashing 30 => 251
Hashing 31 => 148
Hashing 32 => 253
Hashing 33 => 0
Hashing 34 => 1
Hashing 35 => 2
Hashing 36 => 161
Hashing 37 => 163
Hashing 38 => 8
Hashing 39 => 4
Hashing 40 => 148
Hashing 41 => 253
Hashing 42 => 0
Hashing 43 => 1
Hashing 44 => 2
Hashing 45 => 161
Hashing 46 => 163
Hashing 47 => 8
Hashing 48 => 4
Hashing 49 => 9
Hashing 50 => 253
Hashing 51 => 0
Hashing 52 => 1
Hashing 53 => 2
Hashing 54 => 161
Hashing 55 => 163
Hashing 56 => 8
Hashing 57 => 4
Hashing 58 => 9
Hashing 59 => 11
Hashing 60 => 0
Hashing 61 => 1
Hashing 62 => 2
Hashing 63 => 161
Hashing 64 => 163
Hashing 65 => 8
Hashing 66 => 4
Hashing 67 => 9
Hashing 68 => 11
Hashing 69 => 4
Hashing 70 => 1
Hashing 71 => 2
Hashing 72 => 161
Hashing 73 => 163
Hashing 74 => 8
Hashing 75 => 4
Hashing 76 => 9
Hashing 77 => 11
Hashing 78 => 4
Hashing 79 => 3
Hashing 80 => 2
Hashing 81 => 161
Hashing 82 => 163
Hashing 83 => 8
Hashing 84 => 4
Hashing 85 => 9
Hashing 86 => 11
Hashing 87 => 4
Hashing 88 => 3
Hashing 89 => 17
Hashing 90 => 161
Hashing 91 => 163
Hashing 92 => 8
Hashing 93 => 4
Hashing 94 => 9
Hashing 95 => 11
Hashing 96 => 4
Hashing 97 => 3
Hashing 98 => 17
Hashing 99 => 13
Hashing 100 => 246
Hashing 101 => 248
Hashing 102 => 49
Hashing 103 => 44
Hashing 104 => 255
Hashing 105 => 198
Hashing 106 => 43
Hashing 107 => 51
Hashing 108 => 202
Hashing 109 => 2
Hashing 110 => 248
Hashing 111 => 49
Hashing 112 => 44
Hashing 113 => 255
Hashing 114 => 198
Hashing 115 => 43
Hashing 116 => 51
Hashing 117 => 202
Hashing 118 => 2
Hashing 119 => 51
Hashing 120 => 49
Hashing 121 => 44
Hashing 122 => 255
Hashing 123 => 198
Hashing 124 => 43
Hashing 125 => 51
Hashing 126 => 202
Hashing 127 => 2
Hashing 128 => 51
Hashing 129 => 53
Hashing 130 => 44
Hashing 131 => 255
Hashing 132 => 198
Hashing 133 => 43
Hashing 134 => 51
Hashing 135 => 202
Hashing 136 => 2
Hashing 137 => 51
Hashing 138 => 53
Hashing 139 => 55
Hashing 140 => 255
Hashing 141 => 198
Hashing 142 => 43
Hashing 143 => 51
Hashing 144 => 202
Hashing 145 => 2
Hashing 146 => 51
Hashing 147 => 53
Hashing 148 => 55
Hashing 149 => 58
Hashing 150 => 198
Hashing 151 => 43
Hashing 152 => 51
Hashing 153 => 202
Hashing 154 => 2
Hashing 155 => 51
Hashing 156 => 53
Hashing 157 => 55
Hashing 158 => 58
Hashing 159 => 0
Hashing 160 => 43
Hashing 161 => 51
Hashing 162 => 202
Hashing 163 => 2
Hashing 164 => 51
Hashing 165 => 53
Hashing 166 => 55
Hashing 167 => 58
Hashing 168 => 0
Hashing 169 => 209
Hashing 170 => 51
Hashing 171 => 202
Hashing 172 => 2
Hashing 173 => 51
Hashing 174 => 53
Hashing 175 => 55
Hashing 176 => 58
Hashing 177 => 0
Hashing 178 => 209
Hashing 179 => 216
Hashing 180 => 202
Hashing 181 => 2
Hashing 182 => 51
Hashing 183 => 53
Hashing 184 => 55
Hashing 185 => 58
Hashing 186 => 0
Hashing 187 => 209
Hashing 188 => 216
Hashing 189 => 219
Hashing 190 => 2
Hashing 191 => 51
Hashing 192 => 53
Hashing 193 => 55
Hashing 194 => 58
Hashing 195 => 0
Hashing 196 => 209
Hashing 197 => 216
Hashing 198 => 219
Hashing 199 => 220
Hashing 200 => 248
Hashing 201 => 49
Hashing 202 => 44
Hashing 203 => 255
Hashing 204 => 198
Hashing 205 => 43
Hashing 206 => 51
Hashing 207 => 202
Hashing 208 => 2
Hashing 209 => 51
Hashing 210 => 49
Hashing 211 => 44
Hashing 212 => 255
Hashing 213 => 198
Hashing 214 => 43
Hashing 215 => 51
Hashing 216 => 202
Hashing 217 => 2
Hashing 218 => 51
Hashing 219 => 53
Hashing 220 => 44
Hashing 221 => 255
Hashing 222 => 198
Hashing 223 => 43
Hashing 224 => 51
Hashing 225 => 202
Hashing 226 => 2
Hashing 227 => 51
Hashing 228 => 53
Hashing 229 => 55
Hashing 230 => 255
Hashing 231 => 198
Hashing 232 => 43
Hashing 233 => 51
Hashing 234 => 202
Hashing 235 => 2
Hashing 236 => 51
Hashing 237 => 53
Hashing 238 => 55
Hashing 239 => 58
Hashing 240 => 198
Hashing 241 => 43
Hashing 242 => 51
Hashing 243 => 202
Hashing 244 => 2
Hashing 245 => 51
Hashing 246 => 53
Hashing 247 => 55
Hashing 248 => 58
Hashing 249 => 0
Hashing 250 => 43
Hashing 251 => 51
Hashing 252 => 202
Hashing 253 => 2
Hashing 254 => 51
Hashing 255 => 53

여전히 거친 패턴이 있지만 기본 기능 (이미 약한)보다 큰 패턴은 아닙니다.

것을 그러나주의 03그들이 하나의 실행에없는에도 불구하고, 충돌을했다. 그것은 내가 전에 말한 것의 적용입니다 (충돌 저항은 모든 입력 세트에 대해 동일하게 유지되지만 기본 알고리즘의 결함으로 인해 특정 충돌 경로가 열릴 수 있습니다).

TL / DR 섹션

각 반복에 입력을 피드백함으로써 이전 반복에서 발생했을 수있는 충돌을 효과적으로 차단합니다.

따라서 이론적 으로 적어도 이론적으로는md5($input . md5($input)); 이어야합니다 .md5($input)

이것이 중요합니까?

예. 이것이 RFC 2898 에서 PBKDF2가 PBKDF1을 대체 한 이유 중 하나입니다 . 두 가지의 내부 루프를 고려하십시오.

PBKDF1 :

T_1 = Hash (P || S) ,
T_2 = Hash (T_1) ,
...
T_c = Hash (T_{c-1}) 

c반복 횟수는 어디에 P있고, 비밀번호 S는 소금입니다

PBKDF2 :

U_1 = PRF (P, S || INT (i)) ,
U_2 = PRF (P, U_1) ,
...
U_c = PRF (P, U_{c-1})

PRF는 실제로 HMAC입니다. 그러나 여기서 우리의 목적을 위해, 단지 PRF(P, S) = Hash(P || S)두 개의 입력의 PRF는 두 개의 연결을 가진 해시와 동일합니다. 그것은별로 많지 는 않지만 우리의 목적으로는 그렇습니다.

따라서 PBKDF2는 기본 Hash기능 의 충돌 저항을 유지하지만 PBKDF1은 그렇지 않습니다.

함께 묶기 :

우리는 해시를 반복하는 안전한 방법을 알고 있습니다. 사실로:

$hash = $input;
$i = 10000;
do {
   $hash = hash($input . $hash);
} while ($i-- > 0);

일반적으로 안전합니다.

이제 해시를 원하는 이유알아보기 위해 엔트로피 움직임을 분석해 봅시다.

해시는 무한 세트를 취하고 S(∞)더 작고 일관된 크기의 세트를 생성합니다 S(n). 다음 반복 (입력 가정은 다시 전달됩니다) 매핑 S(∞)S(n)다시 :

S(∞) -> S(n)
S(∞) -> S(n)
S(∞) -> S(n)
S(∞) -> S(n)
S(∞) -> S(n)
S(∞) -> S(n)

최종 출력은 첫 번째 출력과 정확히 같은 양의 엔트로피를 갖습니다 . 반복 해도 "더 이상 가려 지지 않습니다 ". 엔트로피는 동일합니다. 예측할 수없는 마법의 원천은 없습니다 (임의 함수가 아닌 의사 랜덤 함수 임).

그러나 반복에는 이점이 있습니다. 해싱 프로세스를 인위적으로 느리게 만듭니다. 그리고 이것이 반복이 좋은 생각 일 수있는 이유입니다. 사실, 그것은 가장 현대적인 암호 해싱 알고리즘의 기본 원칙입니다 (무언가를 반복해서 수행하면 속도가 느려집니다).

1 차 보안 위협 인 무차별 대항과 싸우기 때문에 속도가 느립니다. 해시 알고리즘을 느리게 만들수록 공격자는 우리로부터 도난당한 비밀번호 해시를 공격하기 위해 더 열심히 노력해야합니다. 그리고 그것은 좋은 것입니다 !!!


1
$output = md5($output); // < 2^128 possibilities--- 정말 엄격하다 <, 또는 <=?
zerkms

2
@ zerkms : 그것은 엄격하게 아무것도 아닙니다. md5()확실하게 알기 위해서는 기본 기능 ( 이 경우) 에 대한 매우 구체적인 세부 사항을 알아야합니다. 그러나 일반적으로는 될 것 <이 아니라 <=우리가 세트의 크기에 대해 얘기하고, 기억 ... $output에 대한 모든 가능한 $inputs. 우리는 심지어 그래서 만약 하나 개의 충돌이 될 것입니다 <, 그러므로 <더 나은 generalizer입니다.
ircmaxell

2
@ TomášFejfar 질문은 일반적인 충돌에 관한 것이 아니라 엄격한 출력 세트에서의 충돌 (2 ^ 128 출력, 각각 정확히 128 비트 폭)이라고 생각합니다. 즉 단사 수 있지만 지금까지 내가 일반적인 증거를 알고 (만 증명별로 예를 들어 특정 알고리즘의 충돌을) 할 수 없습니다. 입력이 128 비트 인 경우 단순히 입력을 반환하는 해시 함수를 고려하십시오 (그렇지 않으면 해시). 일반적으로 그것은 의심 스러울 것이나, 그것의 산출물을 먹일 때 그것은 항상 주입적일 것입니다 ... 그것은 논쟁의 요점입니다 ...
ircmaxell


6
Dan과 ircmaxell 사이의 논의가 어떻게 끝났는지 확인하지 않아도 시간을 절약하고 싶은 사람들은 Dan 이 ircmaxell에 동의 한 것으로 끝났습니다 .
jeromej

51

예, 다시 해싱하면 검색 공간이 줄어들지 만 중요하지 않습니다. 효과적인 축소는 중요하지 않습니다.

재해 싱은 무차별 대입에 걸리는 시간을 늘리지 만 두 번만하는 것도 차선책입니다.

실제로 원하는 것은 PBKDF2로 암호를 해시하는 것 입니다. 소금과 반복으로 보안 해시를 사용하는 입증 된 방법입니다. 이 SO 응답을 확인하십시오 .

편집 : 나는 거의 잊었다 -MD5를 사용하지 마십시오! SHA-2 제품군 (SHA-256, SHA-384 및 SHA-512)과 같은 최신 암호화 해시를 사용하십시오.


2
@DFTR-동의했습니다. bcrypt 또는 scrypt가 더 나은 옵션입니다.
orip

쉽게 깨질 수있는 것 (SHA-2 제품군)을 사용하지 마십시오 . crackstation.net 에서 증거를 확인하십시오 . KDF (Key Derivation Function) 기반 암호화 해시 함수 인 scrypt 또는 PBKDF2를 사용하는 경우.
theodore

3
2016 년에 Argon2와 scrypt는 모두가 사용해야하는 것입니다.
silkfire

10

예-문자열과 일치하는 가능한 문자열 수를 줄입니다.

이미 언급했듯이 소금에 절인 해시가 훨씬 좋습니다.

http://websecurity.ro/blog/2007/11/02/md5md5-vs-md5/ 기사 는 왜 동등한 지에 대한 증거를 시도하지만 논리에 대해서는 잘 모르겠습니다. 부분적으로 그들은 md5 (md5 (text))를 분석 할 수있는 소프트웨어가 없다고 가정하지만, 레인보우 테이블을 생성하는 것은 매우 사소한 일입니다.

나는 여전히 md5 (text) 해시보다 md5 (md5 (text)) 유형 해시 수가 적어 충돌 가능성을 높이고 (아직 가능성이 적더라도) 검색 공간을 줄인다는 내 대답을 고수하고 있습니다.


5

대부분의 답변은 암호화 또는 보안에 대한 배경 지식이없는 사람들이합니다. 그리고 그들은 틀 렸습니다. 가능하면 레코드마다 고유 한 소금을 사용하십시오. MD5 / SHA / etc는 너무 빠르며 원하는 것과 반대입니다. PBKDF2와 bcrypt는 느리지 만 (아주 좋지만) ASIC / FPGA / GPU (현재는 매우 저렴한 가격)로 패배 할 수 있습니다. 따라서 메모리 하드 알고리즘이 필요합니다 : enter scrypt .

다음 은 소금과 속도에 대한 평신도 설명 입니다 (메모리 하드 알고리즘은 아님).


4

나는 이것을 실용적인 관점에서 본다. 해커는 무엇입니까? 해시 함수를 통해 문자를 조합하여 원하는 해시를 생성하는 이유는 무엇입니까?

마지막 해시 만 저장하므로 해커는 하나의 해시 만 무력화하면됩니다. 각 bruteforce 단계에서 원하는 해시에서 거의 같은 확률로 넘어 질 확률이 있다고 가정하면 해시 수는 관련이 없습니다. 백만 번의 해시 반복을 수행 할 수 있으며 줄 끝에는 여전히 해시가 하나만 있고 보안을 깨뜨릴 확률은 해시와 동일하기 때문에 보안을 1 비트 증가 또는 감소시키지 않습니다.

어쩌면 이전 포스터는 입력이 적절하다고 생각합니다. 그렇지 않습니다. 해시 함수에 넣은 내용이 원하는 해시를 생성하는 한 올바른 입력 또는 잘못된 입력을 제공합니다.

이제 무지개 테이블은 또 다른 이야기입니다. 레인보우 테이블에는 원시 암호 만 포함되므로 모든 해시의 모든 해시를 포함하는 레인보우 테이블이 너무 커서 해시를 두 번 해치는 것이 좋은 보안 수단이 될 수 있습니다.

물론 OP가 제공 한 예제 만 고려하고 있습니다. 일반 텍스트 암호 만 해시됩니다. 해시에 사용자 이름이나 소금을 포함하면 다른 이야기입니다. 레인보우 테이블은 이미 너무 커서 실용적이지 않고 올바른 해시를 포함하기 때문에 두 번 해싱하는 것은 완전히 불필요합니다.

어쨌든, 여기의 보안 전문가는 아니지만 그것은 제가 경험 한 바에 불과합니다.


이 대답은 모든면에서 잘못되었습니다. 1. 다음 해시를 알면 공격자에게 가치가 없습니다. 반복 된 해시의 입력은 password 이므로 여러 번 해시됩니다 (한 번이 아님). 2. 입력 공간은 비밀번호이고 출력 공간은 해시 비밀번호입니다. 일반적인 비밀번호 의 공간은 출력 공간보다 훨씬 작습니다. 3. 무염 이중 해시 암호 용 레인보우 테이블은 무염 단일 해싱 암호 용 레인보우 테이블보다 크지 않습니다. 4. 사용자 이름은 엔트로피가 적고 좋은 소금은 무작위입니다. 5. 솔팅은 반복을 대체하지 않습니다. 둘 다 필요합니다.
Clement Cherlin 12

3

내가 읽은 것에서 실제로 암호를 수백 또는 수천 번 다시 해시하는 것이 좋습니다.

아이디어는 암호를 인코딩하는 데 더 많은 시간이 걸리면 공격자가 암호를 해독하기 위해 많은 추측을 수행하는 것이 더 많은 작업이라는 것입니다. 그것은 다시 해싱의 이점으로 보입니다. 암호로 더 안전하지는 않지만 사전 공격을 생성하는 데 더 오래 걸립니다.

물론 컴퓨터는 항상 빨라지므로 시간이 지남에 따라이 이점이 줄어 듭니다 (또는 반복 횟수를 늘려야 함).


나도 다른 의견이 언급하지만, en.wikipedia.org/wiki/Key_stretching

2

개인적으로 여러 해시를 신경 쓰지 않지만 암호 와 같은 UserName (또는 다른 사용자 ID 필드) 과 암호를 해시해야하므로 동일한 암호를 가진 두 명의 사용자가 동일한 해시로 끝나지 않습니다. 또한 아마 좋은 상수를 위해 입력 문자열에 다른 상수 문자열을 던질 것입니다.

$hashed_password = md5( "xxx" + "|" + user_name + "|" + plaintext_password);

13
실제로, 상수가 아니라 각 사용자에 대해 무작위로 생성 된 문자열이어야합니다.
Bill the Lizard

7
제안 된대로 사용자 이름을 던지면 일정한 비밀이 작동하고 작업하기가 더 쉽습니다. 기본적으로 임의의 사용자 별 키가 생성됩니다.
SquareCog

4
끊임없는 비밀 소금은 모호함을 통한 안전입니다. "비밀"이 "xxx"+ username + password를 사용하고 있다는 사실을 알게되면 공격자는 테이블에서 데이터를 공격 할 필요조차 없습니다.
Bill the Lizard

8
나는 그것이 모호함을 통한 보안이라고 생각하지 않습니다. 솔트를 사용하는 이유는 여러 md5 해시에 대해 레인보우 테이블을 동시에 계산할 수 없기 때문입니다. "xxx"+ 비밀번호 (동일한 소금)에 대한 1 번 빌딩이 한 번 발생합니다. "xxx"+ username + password에 대한 테이블을 작성하는 것이 무차별 강제보다 나쁩니다.
FryGuy

5
@Bliz the Lizard : "특정 사용자 이름을 공격하기 위해 하나의 사전을 구축하는 공격이 줄어 듭니다"는 무차별 대입 공격입니다 (실제로 더 나쁜 것은 저장해야하는 모든 해시를 계산하기 때문에 더 나쁩니다). 이 경우 완벽하게.
Kornel

2

해시 알고리즘을 사용한다고 가정 해 봅시다 : rot13을 계산하고 처음 10자를 취하십시오. 두 번 (또는 심지어 2000 번) 그렇게하면 더 빠르지 만 동일한 결과를 제공하는 함수를 만들 수 있습니다 (즉, 처음 10 자만 가져 가십시오).

마찬가지로 반복 된 해싱 함수와 동일한 출력을 제공하는 더 빠른 함수를 만드는 것이 가능할 수 있습니다. 따라서 여러분이 선택한 해싱 기능은 매우 중요합니다. rot13 예제와 마찬가지로 해싱을 반복해도 보안이 향상되는 것은 아닙니다. 알고리즘이 재귀 용으로 설계되었다는 연구가 없다면 추가 보호 기능을 제공하지 않는다고 가정하는 것이 더 안전합니다.

즉, 가장 간단한 해싱 기능을 제외하고는 암호화 전문가가 더 빠른 기능을 계산해야 할 가능성이 높습니다. 따라서 암호화 전문가에 액세스 할 수없는 공격자를 막는 경우 반복적 인 해싱 기능을 사용하는 것이 실제로 더 안전 할 것입니다 .


1

일반적으로 더블 해시 또는 이중 암호화에 대한 추가 보안을 제공하지 않습니다. 해시를 한 번 끊을 수 있으면 다시 해시 할 수 있습니다. 그러나 일반적으로 보안을 해치지 않습니다.

MD5를 사용하는 예에서 충돌 문제가 있음을 알고있을 것입니다. "이중 해싱"은 동일한 충돌로 인해 여전히 동일한 첫 번째 해시가 발생하므로 MD5를 다시 두 번째 해시로 가져올 수 있기 때문에이를 방지하는 데 실제로 도움이되지 않습니다.

이렇게하면 "역 MD5 데이터베이스"와 같은 사전 공격으로부터 보호 할 수 있지만 솔트도 마찬가지입니다.

접선에서 이중 암호화를 사용하면 추가 보안이 제공되지 않습니다. 실제로 사용되는 두 키의 조합 인 다른 키가 생성되기 때문입니다. 따라서 두 개의 키를 실제로 찾을 필요가 없기 때문에 "키"를 찾는 노력이 두 배가되지 않습니다. 해시의 결과는 일반적으로 원래 입력과 길이가 다르기 때문에 해싱에는 해당되지 않습니다.


1
모든 것이 맞지만 MD5에 대한 강력한 충돌 저항 타협의 효과는 약간 비례하지 않습니다. 암호 해시 함수를 사용하는 대부분의 시나리오는 강한 충돌 저항에 의존하지 않고 단지 약한 저항에 의존합니다. 이 취약점의 영향을받지 않습니다.
SquareCog

1

이중 해싱은 클라이언트에서 암호를 해시 한 다음 서버에 해당 해시의 해시를 다른 소금으로 저장하는 경우에만 의미가 있습니다.

그렇게하면 누군가 SSL을 제공하는 안전을 무시하고 서버에 침입 한 경우에도 여전히 명확한 암호를 얻을 수 없습니다.

예. 그는 시스템에 침입하는 데 필요한 데이터를 보유하지만 해당 데이터를 사용하여 사용자가 보유한 외부 계정을 손상시킬 수는 없습니다. 그리고 사람들은 거의 모든 것에 동일한 암호를 사용하는 것으로 알려져 있습니다.

그가 명확한 암호를 얻을 수있는 유일한 방법은 클라이언트에 keygen을 설치하는 것입니다. 더 이상 문제가 아닙니다.

간단히 말해 :

  1. 클라이언트의 첫 해싱은 '서버 위반'시나리오에서 사용자를 보호합니다.
  2. 서버의 두 번째 해시는 누군가 데이터베이스 백업을 보유한 경우 시스템을 보호하는 역할을하므로 해당 암호를 사용하여 서비스에 연결할 수 없습니다.

1
+1 클라이언트에 일반 텍스트 암호를 저장하고 싶지 않지만 유선으로 최종 암호화 된 암호를 보내지 않는 것과 같은 시나리오를 생각했기 때문에 이와 같은 대답을 기다리고 있습니다. DB와의 간단한 비교.
Mark

1
웹앱에는 도움이되지 않습니다. 서버가 손상된 경우 서버가 클라이언트로 보내는 코드도 손상됩니다. 공격자는 클라이언트 쪽 해시를 비활성화하고 원시 암호를 캡처합니다.
Clement Cherlin

0

검색 공간을 줄이는 것에 대한 우려는 수학적으로 정확하지만, 검색 공간은 모든 실제 목적 (소금을 사용한다고 가정)에 2 ^ 128로 충분히 크게 유지됩니다. 그러나 우리가 암호에 대해 이야기하고 있기 때문에 봉투 뒤의 계산에 따르면 가능한 16 자 문자열 (영숫자, 대문자, 몇 가지 기호)이 대략 2 ^ 98입니다. 따라서 검색 공간의 인식 감소는 실제로 관련이 없습니다.

그 외에도 암호화 적으로 말하면 차이는 없습니다.

"해시 체인"이라는 암호화 기본 요소가 있지만, 최소한의 시간 동기화만으로 시스템의 무결성을 유지하면서 서명 키를 사용한 후 공개하는 것과 같은 멋진 트릭을 수행 할 수있는 기술입니다. 초기 키 배포 문제를 깨끗하게 회피 할 수 있습니다. 기본적으로, 당신은 해시의 큰 해시 세트를 미리 계산합니다-h (h (h (h (.... (h (k)) ...))), n 번째 값을 사용하여 설정된 간격 후에 서명합니다. 키를 빼고 키 (n-1)를 사용하여 서명하십시오. 수신자는 이제 이전의 모든 메시지를 보냈 음을 확인할 수 있으며, 유효 기간이 지났으므로 서명을 위조 할 수 없습니다.

Bill이 제안한 것처럼 수십만 번을 다시 해싱하는 것은 CPU 낭비 일뿐입니다. 사람들이 128 비트를 깨는 것에 대해 걱정이되면 더 긴 키를 사용하십시오.


1
다시 해싱은 해시 속도를 늦추는 것입니다. 이것은 암호 기반 암호화의 핵심 보안 기능입니다. PCKS5 및 PBKDF2에 대한 링크를 참조하십시오.
orip

0

이 기사의 여러 답변에서 알 수 있듯이 보안을 향상시킬 수있는 경우와 보안을 확실히 해치는 경우가 있습니다. 보안을 확실히 향상시키는 더 나은 솔루션이 있습니다. 해시를 계산하는 횟수를 두 배로 늘리는 대신 소금 크기를 두 배로 늘리거나 해시에서 사용되는 비트 수를 두 배로 늘리거나 두 가지를 모두 수행하십시오! SHA-245 대신 SHA-512로 이동하십시오.


이것은 질문에 대답하지 않습니다.
Bill the Lizard

1
이중 해싱은 노력할 가치가 없지만 해시 크기를 두 배로 늘립니다. 이것이 더 귀중한 생각이라고 생각합니다.
Stefan Rusek

-1

이중 해싱은 공격자가 대부분의 해시를 제시하기 위해 테이블을 구축했을 가능성이 높기 때문에 추악합니다. 해시에 소금을 바르고 해시를 함께 섞는 것이 좋습니다. 해시 (기본적으로 솔트)에 "서명"하는 새로운 스키마가 있지만보다 안전한 방식으로 제공됩니다.


-1

예.

물론 하지 않는 것처럼, 기존의 해시 함수을 여러 번 반복 사용합니다 md5(md5(md5(password))). 에서 가장 당신은 보안의 한계 증가 점점 될 것이다 (이 제공하는 GPU의 공격에 거의 보호와 같은 방식을, 그냥 파이프 라인을.) 최악의 경우, 당신은 당신이 추가 할 때마다 반복하여 해시 공간 (및 보안)을 줄일 수있어 . 보안 상 최악의 상황을 가정하는 것이 현명합니다.

마십시오 암호가 해당이 됐어요있다 사용 디자인 모두 무차별 시간 - 공간 공격에 효과적인 암호 해시, 그리고 저항 할 수있는 능력이 암호 해독에 의해. 여기에는 bcrypt, scrypt 및 일부 상황에서 PBKDF2가 포함됩니다. glibc SHA-256 기반 해시도 허용됩니다.


-1

나는 사지로 나가서 특정 상황에서 더 안전하다고 말할 것입니다 ...하지만 아직 저를 공감하지 마십시오!

수학적 / 암호 적 관점에서 볼 때, 다른 사람이 내가 할 수있는 것보다 더 명확하게 설명 할 것이라고 확신하기 때문에 덜 안전합니다.

하나 MD5 해시 데이터베이스는 MD5보다 "암호"텍스트를 포함 할 가능성이 더 큽니다. 따라서 이중 해싱을 통해 해당 데이터베이스의 효율성을 줄입니다.

물론, 소금을 사용하면이 장점 (불이익?)이 사라집니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.