СЕРВЕР API
Я занимаюсь разработкой API безопасных платежей и хочу избежать атак повторного воспроизведения с помощью манипулирования параметрами в URL.
Перед погружением для решения ваших проблем важно сначала прояснить распространенное заблуждение среди разработчиков, которое относится к знанию разницы между , кто против , что получает доступ к серверу API.
Разница между , кто и , что обращается к серверу API.
Это более подробно обсуждается в этой статье , которую я написал, где мы можем прочитайте:
what - это то, что делает запрос к серверу API. Действительно ли это подлинный экземпляр вашего мобильного приложения, или это бот, автоматизированный скрипт или злоумышленник, вручную копающийся в вашем API-сервере с помощью такого инструмента, как Postman?
who - это пользователь мобильного приложения, которое мы можем аутентифицировать, авторизовать и идентифицировать несколькими способами, например, используя OpenID Connect или потоки OAUTH2.
Если цитируемого текста недостаточно для понимания различий, то Пожалуйста, go впереди и прочитайте весь раздел статьи, потому что, если вы не понимаете это, вы склонны применять менее эффективные меры безопасности на вашем сервере API и клиентах.
СЛОИ БЕЗОПАСНОСТИ И ПАРАМЕТРЫ В URL
Например, в следующем вызове API: https://api.payment.com/wallet/transfer?from_account=123&to_account=456&amount=100
Безопасность - это применение как можно большего количества уровней защиты для создания Атака как можно более тяжелая и трудоемкая, представьте, что она состоит из множества слоев лука, которые нужно очистить, чтобы попасть в центр е.
Злоумышленники всегда будут искать самые легкие цели, нижний висящий фрукт на дереве, потому что они не хотят прибегать к использованию лестницы, когда они могут взять плод с другого дерева с нижним висящим фруктом; )
Таким образом, один из первых уровней защиты состоит в том, чтобы избежать использования параметров в URL для конфиденциальных вызовов, поэтому я бы использовал запрос POST со всеми параметрами в теле запроса, поскольку этот тип запроса не может быть сделано просто скопировать вставить URL-адрес в браузер или любой другой инструмент, таким образом, они требуют больше усилий и знаний для выполнения, так как фрукты находятся выше в дереве для атакующего.
Другая причина заключается в том, что запросы GET попадают в журналы серверов, поэтому могут случайно отображаться и легко воспроизводиться.
REPLAY ATTACKS FOR API CALLS
После выполнения этого вызова API кто-то с достаточно знаний может выполнить один и тот же вызов API, изменив любой из трех параметров на свою собственную ntage.
Да, они могут, и они могут узнать, как работает ваш API, даже если у вас нет документации c для него, им просто нужно почитать его с помощью любого открытого источника. инструмент для мобильных приложений и веб-приложений.
Я думал о выпуске временного токена (токена транзакции) для каждой транзакции. Но это также не звучит как достаточно.
Да, этого недостаточно, потому что этот временный токен может быть украден с помощью атаки MitM, как показано в статье Steal That Api Key With атака «Человек посередине» :
Итак, в этой статье вы узнаете, как настроить и запустить MitM-атаку для перехвата https traffi c в мобильном устройстве под вашим контролем, так что вы можете украсть ключ API. Наконец, на высоком уровне вы увидите, как можно смягчить атаки MitM.
Таким образом, после выполнения атаки MitM для кражи токена легко использовать curl
, Postman
или любые другие подобные инструмент для отправки запросов к API-серверу, как если бы вы были настоящими who и что ожидает API-сервер.
MITIGATE REPLAY ATTACKS
Улучшение существующей защиты безопасности
Я думал о выдаче временного токена (токена транзакции) для каждой транзакции. Но этого также не достаточно.
Этот подход хорош, но его недостаточно, как вы уже заметили, но вы можете улучшить его, если еще не сделали, сделав этот временный токен пригодным для использования только раз.
Еще одна важная мера защиты заключается в том, чтобы запретить повторное выполнение запросов с одинаковым количеством и одинаковыми получателями (from_account
, to_account
), даже если у них есть новый временный токен.
Также не допускайте быстрого выполнения запросов из одного и того же источника, особенно если они предназначены для взаимодействия с человеком.
Эти меры сами по себе не полностью решат проблему, но добавят еще несколько слои в лук.
Использование HMA C для одноразового токена
Чтобы попытаться помочь серверу быть уверенным в who и что делает запрос, вы можете использовать Keyed-Ha sh Код аутентификации сообщения (HMA C) , который предназначен для предотвращения взлома и взлома, и согласно Википедии:
В криптографии HMA C (иногда расширяемый как код аутентификации сообщения с ключом-ха sh или код аутентификации сообщения на основе ха sh) - это определенный c тип аутентификации сообщения катионный код (MA C), включающий функцию криптографии c га sh и секретный ключ криптографии c. Как и в случае любого MA C, его можно использовать для одновременной проверки как целостности данных, так и подлинности сообщения.
Таким образом, клиент может создать токен HMA C с URL-адрес запроса, токен аутентификации пользователя, ваш временный токен и отметка времени, которая также должна присутствовать в заголовке запроса. Затем сервер получит те же данные из запроса и выполнит свой собственный расчет токена HMA C и продолжит выполнение запроса только в том случае, если его собственный результат совпадает с результатом для заголовка токена HMA C в запросе.
В качестве практического примера этого в действии вы можете прочитать часть 1 и часть 2 этой серии блогов о методах защиты API в контексте мобильного приложения, в котором также есть олицетворение веб-приложения. мобильное приложение.
Таким образом, вы можете здесь , как мобильное приложение вычисляет HMA C, а здесь , как сервер Api вычисляет и проверяет его. Но вы также можете увидеть здесь , как веб-приложение подделывает токен HMA C, чтобы заставить сервер API думать, что запросы действительно поступают от who и what Мобильное приложение ожидает его от мобильного приложения.
Код мобильного приложения: :
/**
* Compute an API request HMAC using the given request URL and authorization request header value.
*
* @param context the application context
* @param url the request URL
* @param authHeaderValue the value of the authorization request header
* @return the request HMAC
*/
private fun calculateAPIRequestHMAC(url: URL, authHeaderValue: String): String {
val secret = HMAC_SECRET
var keySpec: SecretKeySpec
// Configure the request HMAC based on the demo stage
when (currentDemoStage) {
DemoStage.API_KEY_PROTECTION, DemoStage.APPROOV_APP_AUTH_PROTECTION -> {
throw IllegalStateException("calculateAPIRequestHMAC() not used in this demo stage")
}
DemoStage.HMAC_STATIC_SECRET_PROTECTION -> {
// Just use the static secret to initialise the key spec for this demo stage
keySpec = SecretKeySpec(Base64.decode(secret, Base64.DEFAULT), "HmacSHA256")
Log.i(TAG, "CALCULATE STATIC HMAC")
}
DemoStage.HMAC_DYNAMIC_SECRET_PROTECTION -> {
Log.i(TAG, "CALCULATE DYNAMIC HMAC")
// Obfuscate the static secret to produce a dynamic secret to initialise the key
// spec for this demo stage
val obfuscatedSecretData = Base64.decode(secret, Base64.DEFAULT)
val shipFastAPIKeyData = loadShipFastAPIKey().toByteArray(Charsets.UTF_8)
for (i in 0 until minOf(obfuscatedSecretData.size, shipFastAPIKeyData.size)) {
obfuscatedSecretData[i] = (obfuscatedSecretData[i].toInt() xor shipFastAPIKeyData[i].toInt()).toByte()
}
val obfuscatedSecret = Base64.encode(obfuscatedSecretData, Base64.DEFAULT)
keySpec = SecretKeySpec(Base64.decode(obfuscatedSecret, Base64.DEFAULT), "HmacSHA256")
}
}
Log.i(TAG, "protocol: ${url.protocol}")
Log.i(TAG, "host: ${url.host}")
Log.i(TAG, "path: ${url.path}")
Log.i(TAG, "Authentication: $authHeaderValue")
// Compute the request HMAC using the HMAC SHA-256 algorithm
val hmac = Mac.getInstance("HmacSHA256")
hmac.init(keySpec)
hmac.update(url.protocol.toByteArray(Charsets.UTF_8))
hmac.update(url.host.toByteArray(Charsets.UTF_8))
hmac.update(url.path.toByteArray(Charsets.UTF_8))
hmac.update(authHeaderValue.toByteArray(Charsets.UTF_8))
return hmac.doFinal().toHex()
}
Код сервера API :
if (DEMO.CURRENT_STAGE == DEMO.STAGES.HMAC_STATIC_SECRET_PROTECTION) {
// Just use the static secret during HMAC verification for this demo stage
hmac = crypto.createHmac('sha256', base64_decoded_hmac_secret)
log.info('---> VALIDATING STATIC HMAC <---')
} else if (DEMO.CURRENT_STAGE == DEMO.STAGES.HMAC_DYNAMIC_SECRET_PROTECTION) {
log.info('---> VALIDATING DYNAMIC HMAC <---')
// Obfuscate the static secret to produce a dynamic secret to use during HMAC
// verification for this demo stage
let obfuscatedSecretData = base64_decoded_hmac_secret
let shipFastAPIKeyData = new Buffer(config.SHIPFAST_API_KEY)
for (let i = 0; i < Math.min(obfuscatedSecretData.length, shipFastAPIKeyData.length); i++) {
obfuscatedSecretData[i] ^= shipFastAPIKeyData[i]
}
let obfuscatedSecret = new Buffer(obfuscatedSecretData).toString('base64')
hmac = crypto.createHmac('sha256', Buffer.from(obfuscatedSecret, 'base64'))
}
let requestProtocol
if (config.SHIPFAST_SERVER_BEHIND_PROXY) {
requestProtocol = req.get(config.SHIPFAST_REQUEST_PROXY_PROTOCOL_HEADER)
} else {
requestProtocol = req.protocol
}
log.info("protocol: " + requestProtocol)
log.info("host: " + req.hostname)
log.info("originalUrl: " + req.originalUrl)
log.info("Authorization: " + req.get('Authorization'))
// Compute the request HMAC using the HMAC SHA-256 algorithm
hmac.update(requestProtocol)
hmac.update(req.hostname)
hmac.update(req.originalUrl)
hmac.update(req.get('Authorization'))
let ourShipFastHMAC = hmac.digest('hex')
// Check to see if our HMAC matches the one sent in the request header
// and send an error response if it doesn't
if (ourShipFastHMAC != requestShipFastHMAC) {
log.error("\tShipFast HMAC invalid: received " + requestShipFastHMAC
+ " but should be " + ourShipFastHMAC)
res.status(403).send()
return
}
log.success("\nValid HMAC.")
Код веб-приложения :
function computeHMAC(url, idToken) {
if (currentDemoStage == DEMO_STAGE.HMAC_STATIC_SECRET_PROTECTION
|| currentDemoStage == DEMO_STAGE.HMAC_DYNAMIC_SECRET_PROTECTION) {
var hmacSecret
if (currentDemoStage == DEMO_STAGE.HMAC_STATIC_SECRET_PROTECTION) {
// Just use the static secret in the HMAC for this demo stage
hmacSecret = HMAC_SECRET
}
else if (currentDemoStage == DEMO_STAGE.HMAC_DYNAMIC_SECRET_PROTECTION) {
// Obfuscate the static secret to produce a dynamic secret to
// use in the HMAC for this demo stage
var staticSecret = HMAC_SECRET
var dynamicSecret = CryptoJS.enc.Base64.parse(staticSecret)
var shipFastAPIKey = CryptoJS.enc.Utf8.parse($("#shipfast-api-key-input").val())
for (var i = 0; i < Math.min(dynamicSecret.words.length, shipFastAPIKey.words.length); i++) {
dynamicSecret.words[i] ^= shipFastAPIKey.words[i]
}
dynamicSecret = CryptoJS.enc.Base64.stringify(dynamicSecret)
hmacSecret = dynamicSecret
}
if (hmacSecret) {
var parser = document.createElement('a')
parser.href = url
var msg = parser.protocol.substring(0, parser.protocol.length - 1)
+ parser.hostname + parser.pathname + idToken
var hmac = CryptoJS.HmacSHA256(msg, CryptoJS.enc.Base64.parse(hmacSecret)).toString(CryptoJS.enc.Hex)
return hmac
}
}
return null
}
ПРИМЕЧАНИЕ : хотя приведенный выше код не использует те же параметры, которые вы используете в вашем случае это хороший начальный указатель для вас, чтобы понять его основы.
Как вы можете видеть, как токен HMA C рассчитывается для мобильного приложения, сервера Api и Веб-приложение идентично семантике логики c, что приводит к тому же токену HMA C, и таким образом веб-приложение может отменить защиту сервера Api и принять только действительный запрос от мобильного приложения.
Суть в том, что все, что вы помещаете в код клиента, может быть переработано для того, чтобы скопировать его в другом клиенте. Так что я должен использовать токены HMA C в моем случае использования? Да, потому что это еще один слой в луке или фрукт выше в дереве.
Могу ли я сделать лучше? Да, вы можете сделать, просто продолжайте читать ...
Улучшение и укрепление безопасности
Может кто-нибудь предложить лучший способ смягчить атаки воспроизведения с подделкой параметров?
Продолжая использовать многоуровневый подход к защите, вы должны обратить внимание на другие многоуровневые подходы, которые позволят вашему API-серверу быть более уверенным в том, кто и хочет получает к нему доступ.
Так что, если клиенты вашего сервера API являются только мобильными приложениями, прочитайте этот ответ на вопрос Как обеспечить безопасность API REST для мобильного приложения? .
В случае, если вам нужно защитить API, который обслуживает как мобильное, так и веб-приложение, посмотрите этот другой ответ на вопрос Несанкционированные вызовы API - Безопасный и разрешить только зарегистрированный веб-интерфейс app .
ПОЛУЧЕНИЕ ДОПОЛНИТЕЛЬНОЙ МИЛИ
Теперь я хотел бы порекомендовать вам отличную работу фонда OW ASP:
Интернет Руководство по тестированию безопасности :
Руководство по тестированию веб-безопасности OW ASP включает в себя инфраструктуру тестирования на проникновение «передовой опыт», которую пользователи могут внедрять в своих организациях, и перо «низкого уровня» Руководство по тестированию etration, в котором описаны методы тестирования наиболее распространенных проблем безопасности веб-приложений и веб-служб.
Руководство по тестированию безопасности мобильных устройств :
Мобильный телефон Руководство по тестированию безопасности (MSTG) - это всеобъемлющее руководство по разработке, тестированию и реверс-инжинирингу безопасности мобильных приложений.