jcoglan
jcoglan on Hacker News
This is the exact thing that wasn't working and which necessitated me sending various test files with particular encodings to a colleague.
Posted by
jcoglan on Thursday January 10, 2013 @04:56PM
from the groups.google.com dept.
I checked, the gem only has the Coffee source, no JavaScript. This frustrates the hell out of me -- clearly the message that you should ship JS is not getting through. When CS can do source maps, ideally people will ship the source, compiled JS and map together.
Having either in a gem is rare, but sensible if the gem wants to include a client to interact with the service it provides.
It is much less safe. PhantomJS has a filesystem API, which is fine when you consider that its primary use case is testing code you wrote and reporting the results. However, given that it is fairly easy to create a bridge between the PhantomJS and WebKit JavaScript runtimes, or to exploit common patterns for making such a bridge, running arbitrary 3rd-party code, loaded over a connection not verified by an SSL cert chain, is asking for trouble. This is an obvious backdoor to get write access to Meteor servers.
Never mind the cost. They are proposing that you run your app, with its 3rd-party jquery/facebook/twitter/google code loaded dynamically over non-SSL connections on a platform with filesystem-write-access to your server.
Thanks for the suggestion in the last paragraph, very helpful.
Dear Christ just kill me already.
Relying on the way you happen to combine data, instead of using a function that's designed for authentication and has baked-in a safe way to combine the inputs, is a bad idea. "What if $EDGE_CASE_OF_INAPPROPRIATE_CRYPTO_FUNCTION" is never a good question to ask. Just use the right tools in the first place.
Your point about padding and length bytes is spot-on. I actually left this out of the explanation, although it's interesting, because I felt it a useless diversion: something that's safe
except in an easily describable subset of cases is still not fit for purpose when specially designed tools without these problems exist.
Point being, although hash functions might work most of the time, their general construction does not make them safe for this purpose. You can't say something is 'mostly secure' because it works 'most of the time'. The times is doesn't work will affect someone, and for them the system is not secure at all.
Plus, implementations with weird edge cases make for horrible debugging, especially when it comes to security.
Possibly, although it depends on the lifetime of the link and the size of the tag (e.g. SHA-1 is 40 chars, each case requires mean of 8 guesses, so whole thing only requires 320 requests).
Better to have resistance as baked into your crypto as you can, rather than relying on a firewall further up the stack. If the data itself is resilient, no implementation will be able to defeat it efficiently.
In some cases, not letting them know they've failed is nice. But the most common case to look out for is, if your auth/crypto process involves multiple steps, don't return early from it, or do anything that alters its runtime significantly. This leaks useful information in many situations. The course at
http://crypto-class.org is better than me at explaining this stuff, and starts on Monday.
Not
the best, but most string comparison methods are designed to fail as soon as possible, which is not appropriate for this use case. Compare the entire input, or use some misdirection to destroy the relationship between correctness and time.
This is just one of the easier ways. It's effective and easy to code.
You'd think that, but it is possible. Google "timing attacks over the internet".
The timing attack is if you check the tag, that fails, and then you don't do any further request processing. This shortens the request time. It depends quite a lot on what you're actually doing with the message, but in general you want to leak as little info as possible about what's happening during any crypto-related process.
As someone who runs a lot of his own open-source projects (see
http://jcoglan.com), and occasionally contributes to others when I find bugs, the biggest reason I do it is to learn things. Not just to learn programming, and various problem domains, but to practise writing and speaking about my work, writing good documentation, and (crucially) practising API design.
Making something you're intending to ship to strangers (or to yourself writing future projects with your tools) forces you to write good documentation (if you want anyone to use it), which in turn forces you to hone your API design skills. If you're having trouble writing the docs for something, you probably haven't adequately solved the problem yet.
I'm not claiming to have done a great job at this on all my projects, but it's a learning process and occasionally my projects find a user base, which keeps me busy, provides new challenges when they find bugs, and if your code's any good it boosts your reputation as a nice side effect.
This needs to be at the top of the thread, rather than all the formal-logic-obsessed debate-club navel-gazing currently sitting there.
Posted by
jcoglan on Friday December 30, 2011 @08:00PM
from the youtube.com dept.
I've been thinking about this. I might try out making all the primitive functions understand promises. So not lazy evaluation per se, but having the core library transparently deal with asynchronous values. Maybe monads would help but that really requires a decent type system.
I'll fix this so that invalid expressions are reported and car and cdr of () are errors.
Oh, no offense taken. Thanks for pointing out other lisps, I should check them out for alternative ideas.
With regard to maturity, Fargo is one week into development. I'm not even sure if its just a quick hack to show off an idea or if it will become a production language. Certainly I have a lot to learn about compilers and VMs before that happens.
I was going to do call/cc but fibers are cheaper. The implementation is very similar, but because fibers can only be resumed once from the last yield you don't need to copy the stack when yielding and resuming.
They also require the user to explicitly start a fiber. This means when you're not in a fiber you can use a faster stackless engine because you don't need to track the state of the current continuation.
Songkick (YC'07, London) is hiring again. We're a
small company (about 20 people) that's working to improve the live music
scene for everyone involved. We help fans track their favourite artists so
they never miss them live, and we help artists get the word out about
upcoming shows.
In the last year we've seen tremendous growth in our user base and with our
commercial partnerships, including integrations with YouTube and Yahoo!
Search. We just launched our first major label integration with EMI and are
working on plenty more. There's a lot of work to do scaling our team and our
technology stack, making it a really exciting time to join us.
If you, or someone you know, is an experienced and fast-learning developer
into TDD and scalable web services we'd love to hear from you. We have a
great team that's a joy to work with (you may have met a few of us at
Ruby events), and we have a lot of challenging projects on our roadmap.
If you're interested, check out our jobs page at
http://www.songkick.com/info/jobs and get in touch with our COO Pete Smith
at [email protected]
I wrote a Scheme in Ruby that has macros last year:
http://github.com/jcoglan/heist. It's an interpreter, not a compiler, and is extremely dynamic: all functions, macros, special forms are just treated as function calls by the parser. Macros are just a subtype of functions that accept and return syntax nodes before eval-ing the output, and this is done at runtime while running all your regular code.
The rationale is that I wanted to learn how to write an interpreter, particularly some of the more challenging parts like tail recursion, macros, continuations and laziness. It's in Ruby because that's what I know best; it's not intended for production use as a replacement for Ruby, though I have used it myself to do a little code transformation and testing in Ruby apps.
So basically it's a toy, but one that I hope others can learn something from.
You're missing a few details, especially around "primitive" data types. There's not really any such thing in Ruby - everything is an object, but some objects - numbers, booleans, nil - are immutable.
When you copy an object, all you do is copy its set of instance variables, which are just references to other objects. For an array, the instance variables are its set of indexes, which again are just references. Copying an array just means making a new list of references, but the objects they point to remain unmodified and uncopied.
Consider:
<pre>
array = ["foo"]
copy = array.dup
</pre>
array and copy are independently mutable - modifying the index in one does not affect the indexes in the other - but they still both contain references to the single string "foo". Thus:
<pre>
copy.first.gsub! /foo/, "bar"
</pre>
modifies the string referenced by copy, which is the same string referenced by array. So array becomes ["bar"].
If you want a true deep copy, do something like this:
<pre>
def deep_copy(object)
case object
when Array
object.map { |item| deep_copy(item) }
when Hash
object.inject({}) do |hash, (key,value)|
hash[deep_copy(key)] = deep_copy(value)
hash
end
# handle other data structures if need be
else
object.respond_to?(:dup) ? object.dup : object
end
end
</pre>