在 Perl 中有没有办法使用准备好的语句(以防止 SQL 注入(inject))在不到 2 分钟的时间内将 100 万条记录插入 MySQL 表?
有一个在线资源 ( Wikimedia ),我想从中下载一个文件 (dewiktionary-latest-all-titles-in-ns0.gz),其中包含近 100 万个标题文章(每篇文章都是维基词典中一个德语单词的描述)。我想每周检查一次此列表,然后对新的或删除的标题使用react。为此,我想每周自动下载一次此列表并将其插入数据库。
虽然我信任维基媒体,但您永远不要过分信任来自互联网的任何内容。因此,为了防止 SQL 注入(inject)和其他安全问题,我总是在 Perl 中使用准备好的语句,确保 SQL 解释器没有机会将内容解释为代码。
通常我会这样做:
方案一
#!/usr/bin/perl -w
use strict;
use warnings;
use LWP::UserAgent;
use DBI;
# DOWNLOAD FROM INTERNET =========================
# create User-Agent:
my $ua = LWP::UserAgent->new;
# read content from Internet
my $response = $ua->get('https://<rest_of_URL>');
# decode content
my $content = $response->decoded_content;
#turn into a list
my @list = split(/\n/,$content);
# STORE IN DATABASE ==============================
# connect with database (create DataBase-Handle):
my $dbh = DBI->connect(
'DBI:mysql:database=<name_of_DB>;host=localhost',
'<user>','<password>',
{mysql_enable_utf8mb4 => 1}
);
# SQL statement
my $SQL = 'INSERT INTO `mytable`(`word`) VALUES(?)';
# prepare statement (create Statement Handle)
my $SH = $dbh->prepare($SQL);
#execute in a loop
foreach my $word (@list) {
$SH->execute($word);
}
# disconnect from database
$dbh->disconnect;
# end of program
exit(0);
注意这一行(第27行):
my $SQL = 'INSERT INTO `mytable`(`word`) VALUES(?)';
SQL 命令行中有一个问号作为占位符。 在下一行中准备了这个 SQL 命令行(即创建了一个准备好的语句),并在循环中执行了这个语句,这意味着每次都会将一个新值($word)插入到表中,而无需任何执行这个值的机会,因为 SQL 解释器看不到这个值。因此,无论攻击者如何写入我下载的文件,都不会导致代码注入(inject)。
但是:
这很慢。下载在几秒钟内完成,但插入循环运行了四个多小时。
有一个更快的解决方案,它是这样的:
方案二
# The code above the SQL-Statement is exactly
# the same as in the 1st program
#-------------------------------------------------
# SQL statement
my $SQL = 'INSERT INTO `mytable`(`word`) VALUES '; # <== NO '?'!
# attach values in a loop
# initiate comma with empty string
my $comma = '';
foreach my $word (@list) {
# escape escapecharacter
$word =~ s/\\/\\\\/g;
# escape quotes
$word =~ s/'/\\'/g;
# put the value in quotes and then in brackets, add the comma
# and then append it to the SQL command string
$SQL .= $comma."('".$word."')";
# comma must be a comma
$comma = ',';
}
# Now prepare this mega-statement
my $SH = $dbh->prepare($SQL);
# and execute it without any parameter
$SH->execute();
# disconnect from database
$dbh->disconnect;
# end of program
exit(0);
(这是简化的,因为 SQL 语句会变得太长而无法被 MySQL 接受。您需要将其拆分为大约 5000 个值的部分并执行它们。但这对于我在这里谈论的问题。)
这运行得非常快。所有值(新表中的近 100 万行)都在不到 2 分钟内插入,速度快了 100 多倍。
如您所见,我创建了一个大语句,但没有占位符。我将值直接写入 SQL 命令。我只需要转义将被解释为转义字符的反斜杠和将被解释为字符串结尾的单引号。
但是其余的值仍然不 protected 并且对 SQL 解释器可见。潜在的攻击者可能会找到一种方法将 SQL 代码插入到将要执行的值中。这可能会损坏我的数据库,甚至可能会授予攻击者 super 用户权限。 (代码注入(inject)引起的权限提升)
有没有办法像程序 1 中那样使用准备好的语句,即使是像程序 2 中那样动态生成的语句?
或者是否有另一种快速且安全将大量数据插入 MySQL 表的可能性?
最佳答案
您的斜体小注释实际上非常相关:
(This is simplified, since the SQL statement would become too long to be accepted by MySQL. You need to split it up in sections of about 5000 values and execute them. But this is not important for the problem I'm talking about here.)
我认为您的“未经准备的陈述”(不是真正的术语)方法更快,因为您一次批量加载 5000 条记录而不是一条一条地加载,而不是因为它不是准备好的陈述。
尝试使用 5000 个 ? 构建准备好的语句,如下所示:
my $SQL = 'INSERT INTO `mytable`(`word`) VALUES ' . '(?),'x4999 . '(?)';
然后构建一次包含 5000 个单词的列表,并以此执行您准备好的语句。您必须使用最后一批中适当数量的单词的第二个动态生成的准备好的语句来处理最后一组(大概)少于 5000 个单词。
您还可以查看 LOAD DATA INFILE 以进行批量加载。
关于mysql - 快速质量插入的准备语句,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/52025433/
我注意到类定义,如果我打开classMyClass,并在不覆盖的情况下添加一些东西我仍然得到了之前定义的原始方法。添加的新语句扩充了现有语句。但是对于方法定义,我仍然想要与类定义相同的行为,但是当我打开defmy_method时似乎,def中的现有语句和end被覆盖了,我需要重写一遍。那么有什么方法可以使方法定义的行为与定义相同,类似于super,但不一定是子类? 最佳答案 我想您正在寻找alias_method:classAalias_method:old_func,:funcdeffuncold_func#similartoca
文章目录一、概述简介原理模块二、配置Mysql使用版本环境要求1.操作系统2.mysql要求三、配置canal-server离线下载在线下载上传解压修改配置单机配置集群配置分库分表配置1.修改全局配置2.实例配置垂直分库水平分库3.修改group-instance.xml4.启动监听四、配置canal-adapter1修改启动配置2配置映射文件3启动ES数据同步查询所有订阅同步数据同步开关启动4.验证五、配置canal-admin一、概述简介canal是Alibaba旗下的一款开源项目,Java开发。基于数据库增量日志解析,提供增量数据订阅&消费。Git地址:https://github.co
在添加一些空格以使代码更具可读性时(与上面的代码对齐),我遇到了这个:classCdefx42endendm=C.new现在这将给出“错误数量的参数”:m.x*m.x这将给出“语法错误,意外的tSTAR,期待$end”:2/m.x*m.x这里的解析器到底发生了什么?我使用Ruby1.9.2和2.1.5进行了测试。 最佳答案 *用于运算符(42*42)和参数解包(myfun*[42,42])。当你这样做时:m.x*m.x2/m.x*m.xRuby将此解释为参数解包,而不是*运算符(即乘法)。如果您不熟悉它,参数解包(有时也称为“spl
我想从then子句中访问case语句表达式,即food="cheese"casefoodwhen"dip"then"carrotsticks"when"cheese"then"#{expr}crackers"else"mayo"end在这种情况下,expr是食物的当前值(value)。在这种情况下,我知道,我可以简单地访问变量food,但是在某些情况下,该值可能无法再访问(array.shift等)。除了将expr移出到局部变量然后访问它之外,是否有直接访问caseexpr值的方法?罗亚附注我知道这个具体示例很简单,只是一个示例场景。 最佳答案
我正在尝试创建一个带有项目符号字符的Ruby1.9.3字符串。str="•"+"helloworld"但是,当我输入它时,我收到有关非ASCII字符的语法错误。我该怎么做? 最佳答案 你可以把Unicode字符放在那里。str="\u2022"+"helloworld" 关于ruby-如何在Ruby字符串中插入项目符号字符?,我们在StackOverflow上找到一个类似的问题: https://stackoverflow.com/questions/1195
如何在Ruby的if语句中检查bash命令的返回值(true/false)。我想要这样的东西,if("/usr/bin/fswscell>/dev/null2>&1")has_afs="true"elsehas_afs="false"end它会提示以下错误含义,它总是返回true。(irb):5:warning:stringliteralincondition正确的语法是什么?更新:/usr/bin/fswscell寻找afs安装和运行状态。它会抛出这样的字符串,Thisworkstationbelongstocell如果afs没有运行,命令以状态1退出 最
我看到其他人也遇到过类似的问题,但没有一个解决方案对我有用。0.3.14gem与其他gem文件一起存在。我已经完全按照此处指示完成了所有操作:https://github.com/brianmario/mysql2.我仍然得到以下信息。我不知道为什么安装程序指示它找不到include目录,因为我已经检查过它存在。thread.h文件存在,但不在ruby目录中。相反,它在这里:C:\RailsInstaller\DevKit\lib\perl5\5.8\msys\CORE\我正在运行Windows7并尝试在Aptana3中构建我的Rails项目。我的Ruby是1.9.3。$gemin
我最近与一位同事讨论了以下Ruby语法:value=ifa==0"foo"elsifa>42"bar"else"fizz"end我个人并没有看到太多这种逻辑,但我的同事指出,这实际上是一种相当普遍的Rubyism。我试着用谷歌搜索这个主题,但没有找到任何文章、页面或SO问题来讨论它,这让我相信这可能是一种非常实际的技术。然而,另一位同事发现语法令人困惑,而是将上面的逻辑写成这样:ifa==0value="foo"elsifa>42value="bar"elsevalue="fizz"end缺点是value=的重复声明和隐式elsenil的丢失,如果我们想使用它的话。这也感觉它与Ruby
我已经开始使用mysql2gem。我试图弄清楚一些基本的事情——其中之一是如何明确地执行事务(对于批处理操作,比如多个INSERT/UPDATE查询)。在旧的ruby-mysql中,这是我的方法:client=Mysql.real_connect(...)inserts=["INSERTINTO...","UPDATE..WHEREid=..",#etc]client.autocommit(false)inserts.eachdo|ins|beginclient.query(ins)rescue#handleerrorsorabortentirelyendendclient.commi
这段代码没有像我预期的那样执行:casewhen->{false}then"why?"else"ThisiswhatIexpect"end#=>"why?"这也不是casewhen->(x){false}then"why?"else"ThisiswhatIexpect"end#=>"why?"第一个then子句在两种情况下都被执行,这意味着我提供给when子句的lambda没有被调用。我知道无论when子句的主题是什么,都应该调用大小写相等运算符===。我想知道当没有为case提供参数时,===的另一边会发生什么。我在想它可能是nil,但它不可能是:->{false}===nil#=>