F# 中字符串的编译时约束,类似于度量单位 - 这可能吗?
我正在使用 F# 开发 Web 应用程序。考虑保护用户输入字符串免受 SQL、XSS 和其他漏洞的影响。
简而言之,我需要一些编译时约束,使我能够区分纯字符串和表示 SQL、URL、XSS、XHTML 等的字符串。
许多语言都有它,例如 Ruby 的本机字符串插值功能 #{.. .}
.
对于 F#,测量单位似乎做得很好,但它们仅适用于数字类型。
有多种采用运行时 UoM (link),但是我认为这对我的目标来说是一种开销。
我研究过 FSharpPowerPack,似乎很有可能为字符串提出类似的东西:
[<MeasureAnnotatedAbbreviation>] type string<[<Measure>] 'u> = string
// Similarly to Core.LanguagePrimitives.IntrinsicFunctions.retype
[<NoDynamicInvocation>]
let inline retype (x:'T) : 'U = (# "" x : 'U #)
let StringWithMeasure (s: string) : string<'u> = retype s
[<Measure>] type plain
let fromPlain (s: string<plain>) : string =
// of course, this one should be implemented properly
// by invalidating special characters and then assigning a proper UoM
retype s
// Supposedly populated from user input
let userName:string<plain> = StringWithMeasure "John'); DROP TABLE Users; --"
// the following line does not compile
let sql1 = sprintf "SELECT * FROM Users WHERE name='%s';" userName
// the following line compiles fine
let sql2 = sprintf "SELECT * FROM Users WHERE name='%s';" (fromPlain userName)
注意:这只是一个示例;不建议使用SqlParameter
。 :-)
我的问题是:有没有一个像样的图书馆可以做到这一点?有没有可能添加语法糖?
谢谢。
更新 1:我需要编译时约束,谢谢 Daniel。
更新 2:我试图避免任何运行时开销(元组、结构、可区分联合等)。
I'm developing a Web application using F#. Thinking of protecting user input strings from SQL, XSS, and other vulnerabilities.
In two words, I need some compile-time constraints that would allow me discriminate plain strings from those representing SQL, URL, XSS, XHTML, etc.
Many languages have it, e.g. Ruby’s native string-interpolation feature #{...}
.
With F#, it seems that Units of Measure do very well, but they are only available for numeric types.
There are several solutions employing runtime UoM (link), however I think it's an overhead for my goal.
I've looked into FSharpPowerPack, and it seems quite possible to come up with something similar for strings:
[<MeasureAnnotatedAbbreviation>] type string<[<Measure>] 'u> = string
// Similarly to Core.LanguagePrimitives.IntrinsicFunctions.retype
[<NoDynamicInvocation>]
let inline retype (x:'T) : 'U = (# "" x : 'U #)
let StringWithMeasure (s: string) : string<'u> = retype s
[<Measure>] type plain
let fromPlain (s: string<plain>) : string =
// of course, this one should be implemented properly
// by invalidating special characters and then assigning a proper UoM
retype s
// Supposedly populated from user input
let userName:string<plain> = StringWithMeasure "John'); DROP TABLE Users; --"
// the following line does not compile
let sql1 = sprintf "SELECT * FROM Users WHERE name='%s';" userName
// the following line compiles fine
let sql2 = sprintf "SELECT * FROM Users WHERE name='%s';" (fromPlain userName)
Note: It's just a sample; don't suggest using SqlParameter
. :-)
My questions are: Is there a decent library that does it? Is there any possibility to add syntax sugar?
Thanks.
Update 1: I need compile-time constraints, thanks Daniel.
Update 2: I'm trying to avoid any runtime overhead (tuples, structures, discriminated unions, etc).
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(5)
有点晚了(我确信有一种时间格式,2 月 23 日和 11 月 30 日之间只有一点点不同),我相信这些俏皮话适合你的目标:
A bit late (I'm sure there's a time format where there is only one bit different between February 23rd and November 30th), I believe these one-liners are compatible for your goal:
理论上,可以使用“单位”对字符串提供各种编译时检查(该字符串是否“受污染”的用户输入或已清理?该文件名是相对的还是绝对的?...)
在实践中,我已经个人认为它不太实用,因为有太多现有的 API 只使用“字符串”,因此您必须非常小心并手动转换从这里到那里的管道数据。
我确实认为“字符串”是错误的巨大来源,处理字符串污染/规范化等的类型系统将是静态类型减少错误的下一个飞跃之一,但我认为这就像 15-年地平线。不过,我对尝试使用 F# UoM 方法的人们感兴趣,看看他们是否能从中受益!
In theory it's possible to use 'units' to provide various kinds of compile-time checks on strings (is this string 'tainted' user input, or sanitized? is this filename relative or absolute? ...)
In practice, I've personally not found it to be too practical, as there are so many existing APIs that just use 'string' that you have to exercise a ton of care and manual conversions plumbing data from here to there.
I do think that 'strings' are a huge source of errors, and that type systems that deal with taintedness/canonicalization/etc on strings will be one of the next leaps in static typing for reducing errors, but I think that's like a 15-year horizon. I'd be interested in people trying an approach with F# UoM to see if they get any benefit, though!
无法做到这一点的最简单的解决方案
是编写一个具有某种数字类型的类型来包装字符串,例如
然后您对字符串进行编译时检查
The simplest solution to not being able to do
would be to write a type which had some numeric type to wrap the string like
Then you have a compile time check on your strings
很难说出你想做什么。您说您“需要一些运行时约束”,但您希望使用严格的编译时测量单位来解决这个问题。我认为简单的解决方案是创建
SafeXXXString
类(其中XXX
是Sql
、Xml
等)验证他们的输入。它为您提供运行时安全性,而不是编译时安全性。但它很简单、自记录,并且不需要阅读 F# 编译器源代码即可工作。
但是,为了回答你的问题,我没有看到任何方法可以用度量单位来做到这一点。就语法糖而言,您也许可以将其封装在一个 monad 中,但我认为这会使其变得更加笨重,而不是更少。
It's hard to tell what you're trying to do. You said you "need some runtime constraints" but you're hoping to solve this with units of measure, which are strictly compile-time. I think the easy solution is to create
SafeXXXString
classes (whereXXX
isSql
,Xml
, etc.) that validate their input.It gives you run-time, not compile-time, safety. But it's simple, self-documenting, and doesn't require reading the F# compiler source to make it work.
But, to answer your question, I don't see any way to do this with units of measure. As far as syntactic sugar goes, you might be able to encapsulate it in a monad, but I think it will make it more clunky, not less.
您可以使用可区分的联合:
您可以进行编译时检查,并且添加两个经过验证的字符串不会生成经过验证的字符串(度量单位允许)。
如果引用类型增加的开销太大,则可以使用结构体。
You can use discriminated unions:
You get a compile-time check, and adding two validated strings won't generate a validated string (which units of measure would allow).
If the added overhead of the reference types is too big, you can use structs instead.