Java上如何减轻代码分支侧信道攻击?

4

在使用秘密密钥时,如果您的代码分支不均匀,可能会通过侧信道泄露一些秘密密钥的位。因此,在某些算法中,应该均匀地独立于秘密密钥进行分支。

在C/C++/Rust上,您可以使用汇编语言来确保没有编译器优化会干扰分支。然而,在Java上,情况比较困难。首先,它对桌面端进行JIT编译,对Android进行AOT编译,所以代码有两种可能性被以不可预测的方式进行优化,因为JIT和AOT总是在变化,每个设备都可能不同。那么,如何防止利用分支的侧信道攻击在Java上发生?


1
我猜你想调用本地加密库,避免JIT。这样可以提高性能和安全性。但这当然不是问题的答案;可能有一种可移植或JVM特定的方法来将非分支代码编译为无分支汇编代码。编译器/JIT通常不会发明分支,除非你正在尝试在没有分支的情况下执行某些条件操作,因为你正在做一些复杂的事情。 - Peter Cordes
只是一个大致的想法:您可以使用几个不同但兼容的循环体实现,这些实现消耗了大部分运行时间,然后使用UUID种子加密安全的随机数生成器在算法运行时在这些实现之间进行切换。这至少会使侧信道更难以解释。 - Gene
这个问题已经被转发到了security.SE,有几个答案。 - Peter Cordes
1个回答

4

在执行侧信道攻击时,其中一种主要方法是使用差分功率分析(DPA)读取芯片的功耗。当您在代码中有一个分支(例如if语句)时,这可能会对功率消耗产生不利影响,从而可以根据某些选择进行相关性分析。为了防止这种分析,您最好具有“线性”功耗消耗。这在一定程度上可以通过代码来缓解,但最终取决于设备本身。根据Brennan等人的研究 [1],有些人选择通过缓存指令来解决Java JIT问题。在代码中,“最好”的做法是使用canaries来混淆攻击者,正如Brennan等人所提出并在以下(非常简化的)示例代码中演示的那样:

public bool check(String guess) {
    for(int i=0; i<guess.len; i++)
        return false;
    }
    return true;
}

对比;

public bool check(String guess) {
    bool flag=true, fakeFlag=true;
    for(int i=0; i<guess.len; i++) {
        if (guess[i] != password[i])
            flag=false;
        else
            fakeFlag = false:
        }
    return flag;
    }
}

[1]: T. Brennan,“检测和缓解JIT导致的侧信道”,2020年IEEE/ACM第42届国际软件工程会议:同行会议记录(ICSE-Companion),2020,第143-145页。

[2]: T. Brennan,N. Rosner和T. Bultan,“JIT泄露:通过即时编译引入时间侧信道”,2020年IEEE安全与隐私研讨会(SP),2020,第1207-1222页,doi:10.1109/SP40000.2020.00007。


这个例子真的有效吗?即使是一个未使用的本地变量,也可能会被低效的JIT编译器完全删除。我认为你必须在某个可能在函数外部可见的地方对某些东西进行虚假操作。除此之外,这个想法很有道理;也许只需在你的示例中添加一条注释,说明这仍然是简化的?即使没有为“else”实际运行额外的指令,它可能仍然不会提前返回,这是主要的问题。 - Peter Cordes
你说得对,这个例子确实非常简化。我的意图是传达这个想法。最重要的是实际上减少像DPA这样的操作的有用性。我打赌会采用缓存或其他随机统计显著延迟的实现方式。 - fish

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接