计划中的软件升级会导致对Flyway迁移脚本进行更严格的SQL解析.语法需要修复,但这将更改校验和并使Flyway的验证失败.SQL的语义不会改变.有没有在不笨拙地修复数据库的情况下使脚本合法化的方法?
它看起来像一个32位的校验和,因此不太可能是安全的.理想情况下,我希望:
- 在文件顶部的注释中只有几个神奇的可打印的美国ASCII字母
- 不需要我放弃我的SQL
- 由我能理解的代码生成
- 不需要任何特殊硬件或配置
有谁有什么巧妙的技巧吗?
计划中的软件升级会导致对Flyway迁移脚本进行更严格的SQL解析.语法需要修复,但这将更改校验和并使Flyway的验证失败.SQL的语义不会改变.有没有在不笨拙地修复数据库的情况下使脚本合法化的方法?
它看起来像一个32位的校验和,因此不太可能是安全的.理想情况下,我希望:
有谁有什么巧妙的技巧吗?
问得好.除非算法被设计成特别困难,例如bcrypt,否则天真地快速遍历数十亿种可能性,2^32(约40亿)的可能性应该是可行的.事实上,Flyway忽略该脚本,然后应用众所周知的CRC32错误检测代码(整个过程描述为here).
在inverse CRC32 function存在的情况下,更容易对其进行暴力逼迫.这项技术也适用于加密散列.一些CPU具有硬件CRC32加速,从而使这一过程变得更快.文件越长,需要的时间就越长.如果Java有一个更广泛的API,则可以使用在末尾放置封套的字母来加快速度.
下面的代码试图找到一个7个大写字母的解决方案-26^7(~80亿)个猜测.将所需的校验和作为参数传递给程序,并通过标准输入传递源SQL迁移脚本.为方便起见,程序将打印原始文件的Flyway校验和计算,然后在一段时间后打印出它找到的第一个没有新行的解决方案.可能没有任何解决方案(没有针对确切程序本身的解决方案),在这种情况下,请对文件稍作更改,然后重试.
java ReverseFlyway.java 16580903 < V42__add_bark.sql
将字符串100放在要修改文本的位置.
重要的是,SQL的语义不能改变.不幸的是,在保留其校验和的同时更改脚本的语义非常容易.例如,
-- Robert-DROP TABLE Students;
具有与Flyway相同的校验和
-- Robert-
DROP TABLE Students;
(寓意:正常化,不要删除章节.)
Flyway如何实现的确切细节可能会因版本而异.如果你有奇怪的东西,比如BOM,可能需要修改一些东西.
如果你愿意,可以很容易地将代码更改为搜索两三个单词、一些空格和制表符、打油诗或任何你喜欢的东西.
import java.io.*;
import java.util.zip.*;
class ReverseFlyway {
private final Checksum checksum = new CRC32();
private final int target;
private final byte[] data;
public static void main(String[] args) throws IOException {
/** /
new ReverseFlyway("Magic 'XXXXXXX'", Integer.MIN_VALUE);
/*/
String text = loadText();
new ReverseFlyway(text, Integer.parseInt(args[0]));
/**/
}
private ReverseFlyway(String text, int target) {
this.target = target;
this.data = text.getBytes();
System.err.println(checksum());
int magicLen = 7;
int place = text.indexOf("X".repeat(magicLen));
attempt(place, magicLen);
System.err.println("No solutions found");
System.exit(1);
}
private int checksum() {
checksum.reset();
checksum.update(data);
return (/** /short/*/int/**/) checksum.getValue();
}
private void attempt(int place, int remaining) {
if (remaining == 0) {
if (target == checksum()) {
System.out.println(new String(data));
System.exit(0);
}
} else {
for (byte letter = 'A'; letter <= 'Z'; ++letter) {
data[place] = letter;
attempt(place+1, remaining-1);
}
}
}
private static String loadText() throws IOException {
StringBuilder buff = new StringBuilder();
BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
for (;;) {
String line = in.readLine();
if (line == null) {
return buff.toString();
}
buff.append(line);
}
}
}