【问题标题】:A way to validate session cookies without storing them一种无需存储即可验证会话 cookie 的方法
【发布时间】:2018-02-11 16:37:30
【问题描述】:

基本信息

  • 我有一个网络服务器,里面有所有花哨的东西。
  • 它在我的半土豆 PC 上运行,所以我没有任何可用的 64TB SSD 存储空间。
  • 我必须关心可扩展性,因为有一天我可能有 1000 个用户。

    哇,这样的用户,非常多!

  • 如果用户丢失他的设备,我不希望任何密码被盗。所以我决定使用会话。
  • 我不想存储所有用户会话,因为这可能是 100 台设备。所以1000 * 100 是很多会话cookie。
  • 我想到了一种方法不存储,而是验证会话以检查请求是否来自帐户所有者。

结构

全球

// Secret 128 bytes array.
const ByteArray serverMagic;

// Generates a SHA512 hash based on data.
function hash(ByteArray data);

// Encodes array of bytes into base64 string.
function base64(ByteArray data);

// Returns current time on the server.
function getCurrentDate();

// Returns userMagic based on the user ID.
function getUserMagic(Integer uid);

// Returns true if user entered wrong password.
function isLoginInvalid(Integer uid, String password);


function Object.toShortString(); // Encodes an object into a short ASCII string.

针对每个用户

const Integer uid; // User ID.
var String password; // Secret code that gives you all of that damn power.
var ByteArray userMagic // Secret 128 bytes array.

逻辑

如果用户想登录

他将发送:

  • 会话到期日期(因此 cookie 在所需时间后将失效)
  • 用户 ID
  • 密码(或者密码的哈希,没关系)


function logIn(Date expire, Integer uid, String password) {
  if(isLoginInvalid(uid, password)) then
    return error;

  String encExpire = expire.toShortString();
  String encUID = uid.toShortString();

  ByteArray userMagic = getUserMagic(uid);

  ByteArray food = encExpire + '_' + serverMagic + userMagic + '_' + encUID;
  ByteArray hash = hash(food);

  String session = encExpire + '_' + encUID + '_' + base64(hash);
  return session;
}

客户端将保存该会话 cookie。

如果用户会采取一些行动

他会向服务器发送一个会话 cookie。

// After parsing session cookie
function request(Date sessionExpire, Integer uid, ByteArray sessionHash) {
  if(sessionExpire < getCurrentDate()) then
    return error;

  String encExpire = sessionExpire.toShortString();
  String encUID = uid.toShortString();

  ByteArray userMagic = getUserMagic(uid);

  ByteArray food = encExpire + '_' + serverMagic + userMagic + '_' + encUID;
  ByteArray hash = hash(food);

  if(hash == sessionHash) then
    return success;
  else
    return error;
}

如果用户想要使所有会话无效

由于设备丢失或密码更改,服务器只能生成一个新的userMagic,这将使之前的所有会话cookies无效。

问题

  • 不存储会话是存储的好方法吗?
  • 我的系统是否存在缺陷?
  • 能破解吗?
    • 又如何破解?
  • 怎么会被滥用?
  • 如何改进我的系统?

【问题讨论】:

    标签: security session cookies hash backend


    【解决方案1】:

    修改后的答案:

    此类解决方案的问题在于它们通常容易受到非常微妙的错误的影响,例如length extension attacks。安全起见,改用HMAC

    仅供参考 .Net 的一些结构与此差别不大,但使用 HMAC 且没有用户魔法。

    上一个,撤回的答案:

    任何用户都可以计算:

    String session = encExpire + '_' + encUID + '_' + base64(hash);
    

    这意味着任何用户都可以创建任意会话。黑客将 encUID 设置为管理员 uid 来执行此操作,管理员 uid 可能是 1 或 0 或类似的值。

    我可能会建议使用带有密钥的 HMAC 来修复此错误,但您真的不应该自己实现这个。寻找一个广泛使用且经过深入研究的库,它可以为您进行会话管理,而不是尝试重新发明轮子。

    【讨论】:

    • 好吧,任何用户都可以计算出来。但问题是每次会话都会生成新的哈希值。 Plus hash 使用 2 个秘密值,它们永远不会离开服务器机器。 ByteArray food = encExpire + '_' + serverMagic + userMagic + '_' + encUID; 所以,据我所知,用户需要生成一个相等的哈希值。为此,他将需要获取秘密值(他不能)或尝试 2^256 次以生成 SHA512 散列或反转 SHA512 散列。这就是问题所在。在秘密值中。谢谢您的回答。但如果你认为我遗漏了什么,你能纠正我吗?
    • @DanilOzerov 对不起,我明白你的意思,所以我的回答不正确。但是,它可能容易受到length extension attacks 的攻击。 HMAC 可以防止这种情况发生。
    猜你喜欢
    • 2011-02-16
    • 2010-10-01
    • 2017-05-07
    • 2012-04-30
    • 2021-06-28
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多