Av rating:
Total votes: 24
Total comments: 19


Robyn Page and Phil Factor
SQL Server Security Workbench Part 1
06 March 2007

Securing SQL Server : RolePlay!


    Contents
    --------
Preamble
SQL Injection
Other Security Risks
Managing access to Database Objects
The Test-Database Build Script
Reassigning those 'Deny' roles
Taking out the 'permission' donkey work
The Test harness


    Preamble
    --------

SQL Server Security is sometimes rather a blind spot to application
developers. This has been widely illustrated by the success of very
simple attacks on database-driven websites, which would have been
prevented by even moderate security measures

In this workbench we will present two slightly different
security models for a database. One of these uses roles to deny access
to tables and views, and the other relies on withholding permissions
to all objects except the stored procedures that make up the
application-interfece.

So what are these formas of attack on databases that have been so
successful? Well, nothing very clever, just ways of testing whether
any real security has been put in place. SQL Injection attacks are just
one example of the many ways of gaining access to data with  malicious
intent.

    SQL Injection
    -------------

It is impossible, purely from the database layer of an application
alone, to prevent SQL injection attacks happening, but you can render
them harmless as long as you can manage the Usernames that the
application has, in order to access the database. If you are accustomed
to give you application logins DBO rights in your database, then it is
time to tighten security

SQL Injection usually comes through the failure of the application
programmer to filter the input from the user to 'escape' or otherwise
change SQL String-delimiters in input from the user, or pass numbers
unvalidated.

So, when a user types in CM5 4RS for his postcode, then the
application programmer might take it into his, or her, head to
construct, on the fly, the SQL String...*/

INSERT INTO address (customer_id,postcode) VALUES (2421,'CM5 4RS')

/* then it is the work of a moment for a hacker to type the following
into the postcode field of the application*/

--  CM5 4RS') Select * from customer select ('

--  which is then automatically converted into:

INSERT INTO address (customer_id,postcode) VALUES (2421,'CM5 4RS')
SELECT * FROM customer
SELECT ('')
/*
with obvious results, and no apparent error.

Of course, if the application programmer has remembered to 'escape'
the string delimiter "'", then it will probably just trigger a rule
error, (see the Data Validation Workbench for details)
*/

INSERT INTO address (customer_id,postcode) VALUES (2421,'CM5 4RS'')
Select * from customer
select ('''
)
/*
...but any DBA will be wondering how on earth you would have left
the customer table accessible if it has personal information in it

   Other security risks
   --------------------

No password at all!
    The worst risk of all is the one of having an SA Login with no
    password. You may think this never happens, but I came across
    a site recently where exactly this was in place in a production
    manufacturing application, installed by a commercial software
    company, on an intranet. Similar problems happen when obvious
    passwords are in use and never changed.

Nicking the database
    Sometimes one comes across a website using a database such as
    SQLite, where the database file is actually kept in the document
    root of /WWW. It can be downloaded and read with a simple
    HTTP request - and it is in plain text.

Exposed access credentials!
    This simply means leaving the user ID and password in an 'INCLUDE'
    file or embedded in a code file which can be downloaded by a user.
    One should always use integrated security wherever    possible,
    since it does not require any separate authentication of
    the user. It is plain silly to leave the INCLUDE file within the
    document root of /WWW in a web application as it can be gotten
    with a simple HTTP Request. The funniest story I heard was of the
    occasion that a spammer left a user_ID and password to his FTP site
    in code in the body of the Spam he sent out. The FTP site was soon
    empty!

Exposed error reporting
    This just means that if an attacker manages to penetrate the
    database by means of SQL Injection, he can gain a great deal of
    information by means of reading error information that is intended
    for the developer. Error Debug Information must be logged (we
    use Emails to send error information) and not shown to the end user.

Poor Housekeeping
    Even where DBAs have taken a lot of trouble with security, it is
    sometimes possible to find database backups, table-backups or other
    such files lying around, with commercial or personal data in them.

In this workshop we will concentrate on the means to prevent access
to sensitive information within the database itself, from a user
connecting with an application login.

Managing access to database objects 
-----------------------------------

We'll be experimenting with two obvious techniques:

1/ Use the DenyDataReader and DenyDataWriter Database roles and assign
these roles to the users of the application

Advantage:    difficult to defeat
Disadvantage: no access to views or table functions by the application

2/ give access-permissions only to the views and stored procedures that
are to be used by the application.

Advantage:    access to views as well as procedures to the application
Disadvantage: can be compromised where complex security model is in place

Preventing any damage through SQL Injection is a relatively simple
matter. You should not allow access to the base tables by the
application. All database access should be through a set of stored
procedures. This will often cause emotional scenes amongst the
programmers, but the result will be a secure database. There are
occasional instances where the programmers can be justified in
objecting, such as when a widget is directly bound to a table,
in which case the second model can be used.

So, let's set up a sample database and try out these techniques 

The Test-Database Build Script 
 
------------------------------

Firstly create a database called SecurityWorkbench. Then... */

--add a login for the application
EXEC master.dbo.sp_addlogin @loginame = N'Workbench',
        
@passwd = 'mypassword',
        
@defdb = N'SecurityWorkbench',
        
@deflanguage = N'us_english'

EXEC dbo.sp_grantdbaccess
    
@loginame = N'Workbench', @name_in_db = N'Workbench'
GO


--and now we will create a sample data table, just the thing
--that the hacker is after:

--drop table customer
CREATE TABLE [dbo].[Customer](
    
[Customer_ID] [int] IDENTITY(1,1) NOT NULL,
    
[Firstname] [varchar](50) NULL,
    
[Surname] [varchar](50) NOT NULL,
    
[Password] [varchar](50) NULL,
    
[User_ID] [varchar](20) NOT NULL,
    
[CreditCardNo] [char](16) NULL,
    
[SortCode] [varchar](20) NULL,
    
[AccountNo] [varchar](20) NULL,
    
[InsertionDate] [datetime] NOT NULL
        
CONSTRAINT [DF_Customer_InsertionDate]  
        
DEFAULT (GETDATE())
)
ON [PRIMARY]


/* Now we create a stored procedure that checks the User ID and
password of the user, so it is unnecessary to expose the information
outside the database */

CREATE PROCEDURE spLogMeIn
@User_ID VARCHAR(50),
@Password VARCHAR(50),
@Success INT output
AS
BEGIN
    
-- SET NOCOUNT ON added to prevent extra result sets from
    -- interfering with SELECT statements.
SET NOCOUNT ON;
SELECT @success = CASE WHEN EXISTS (SELECT 1 FROM customer
            
WHERE [user_ID] =@User_ID AND Password=@password
            
) THEN -1 ELSE 0 END
END


--pop some spoof data into the table....
INSERT INTO Customer
    
(Firstname, Surname, USER_ID, Password,
        
Creditcardno, SortCode, AccountNo)
SELECT 'Joe', 'McTavish','Foo','plasticShoe',
        
'7666923165777980','23-45-67','040592739'
INSERT INTO Customer
    
(Firstname, Surname, USER_ID, Password,
        
Creditcardno, SortCode, AccountNo)
SELECT 'Lars', 'Porsenna','Abe','ninegods',
        
'5960711184930897','76-54-23','014354678'
INSERT INTO Customer
    
(Firstname, Surname, USER_ID, Password,
        
Creditcardno, SortCode, AccountNo)
SELECT 'Abou', 'Ben-Adam','Tribe','increase',
        
'9807493817364950','08-48-37','003948673'
INSERT INTO Customer
    
(Firstname, Surname, USER_ID, Password,
        
Creditcardno, SortCode, AccountNo)
SELECT 'Phil', 'Factor','jig','flutersball',
        
'7666923165777980','22-45-44','020594835'

/* Now it is possible that we will want to allow the application access
to a view of the customer information without the sensitive
information.
Here is a simple view to illustrate such a view....*/
CREATE VIEW [dbo].[vCustomer]
AS
SELECT    
Customer_ID, Firstname, Surname, USER_ID, InsertionDate
FROM         dbo.Customer

GO
--now, all we need to do is to grant access to it by the application
GRANT INSERT ON [dbo].[vCustomer] TO [Workbench]
GO
GRANT REFERENCES ON [dbo].[vCustomer] TO [Workbench]
GO
GRANT SELECT ON [dbo].[vCustomer] TO [Workbench]
GO
GRANT UPDATE ON [dbo].[vCustomer] TO [Workbench]
GO

/* We'll also create a procedure that returns a table that would
provide a recordset to .NET
*/
CREATE PROCEDURE spCustomer
AS
SELECT    
Customer_ID, Firstname, Surname, USER_ID, InsertionDate
FROM         dbo.Customer

GO

--and grant access to it, by the application
GRANT EXECUTE ON [dbo].[spcustomer] TO [Workbench]

/* and now we do a stored procedure which uses 'Dynamic' SQL. This
will serve to show how Dynamic SQL can cause problems */
CREATE PROCEDURE spCustomerWithDynamicSQL
AS
EXECUTE
('SELECT  Customer_ID, Firstname, Surname, User_ID,
    InsertionDate FROM dbo.Customer'
)

GO
--and grant access to it, by the application
GRANT EXECUTE ON [dbo].[spCustomerWithDynamicSQL] TO [Workbench]

/* and we ought to experiment with a function, to see what happens
in this case...*/

CREATE  FUNCTION [dbo].[uftCustomer]
(
)
RETURNS
@Results TABLE
(
    
[Customer_ID] [int] ,
    
[Firstname] [varchar](50),
    
[Surname] [varchar](50),
    
[User_ID] [varchar](20),
    
[InsertionDate] [datetime]
)
AS
BEGIN
INSERT INTO
@Results
    
(Customer_ID, Firstname,Surname,[User_ID],InsertionDate)
SELECT Customer_ID, Firstname,Surname,[User_ID],InsertionDate
    
FROM customer
RETURN
END
GO
GRANT SELECT ON [dbo].[uftCustomer] TO [Workbench]

/* ..and a view that encapsulates a function just to test out
what happens when you change permissions */
CREATE VIEW [dbo].[vCustomerViaFunction]
AS
SELECT    
Customer_ID, Firstname, Surname, USER_ID, InsertionDate
FROM         dbo.uftCustomer()

GO
GRANT SELECT ON [dbo].[vCustomerViaFunction] TO [Workbench] /* 


   Reassigning those 'Deny' roles
   ------------------------------

the following block of code will drop the assignment
of all roles to our User, but assign him to the
denydatawriter and denydatareader role*/

--'blanket' method to deny rights to make changes to tables
EXEC sp_droprolemember N'db_accessadmin', N'Workbench'
EXEC sp_droprolemember N'db_datawriter', N'Workbench'
EXEC sp_droprolemember N'db_datareader', N'Workbench'
EXEC sp_droprolemember N'db_owner', N'Workbench'
EXEC sp_droprolemember N'db_ddladmin', N'Workbench'
EXEC sp_droprolemember N'db_backupoperator', N'Workbench'
EXEC sp_droprolemember N'db_securityadmin', N'Workbench'
EXEC sp_addrolemember N'db_denydatawriter', N'Workbench'
EXEC sp_addrolemember N'db_denydatareader', N'Workbench'
GO
/*
Now try accessing the various objects we have created, using
the code I've provided later on in the workbench. To do so
keep this code open in one window, and open a new window
using the Workbench login and mypassword password. Then
paste in the code inm the section entitled 'Try out accessing
the test database'
-------------------------------------------------------------

When you've done that, execute the following code and see
what differences this makes. The following block of code will
rescind the role that denies rights to select or make changes
to tables*/


EXEC sp_droprolemember N'db_denydatareader', N'Workbench'
--and to read from them
EXEC sp_droprolemember N'db_denydatawriter', N'Workbench'
GO
/*
Now try accessing the various objects we have created, using
the code I've provided later on in the workbench.


    Taking out the 'permission' donkey work
    ---------------------------------------

Now the problem with implementing this sort of level of
security is that your database probably has loads
of tables and stored procedures and going through SSMS,
doing everything by hand is going to be a nightmare. So here
is a simple stored procedure that sets the permissions for
the user you wish. */

CREATE PROCEDURE spDoAllPermissions
@name VARCHAR(100)
/*
spDoAllPermissions  'workbench'
*/
AS
DECLARE
@Command VARCHAR(8000)--Varchar(MAX) in SQL Server 2005
SELECT @command=COALESCE(@command,'') + '
'
+ CASE Table_Type WHEN 'BASE TABLE' THEN
'
REVOKE INSERT ON ['
+TABLE_SCHEMA+'].['+TABLE_NAME+'] TO ['+@name+']
REVOKE DELETE ON ['
+TABLE_SCHEMA+'].['+TABLE_NAME+'] TO ['+@name+']
REVOKE REFERENCES ON ['
+TABLE_SCHEMA+'].['+TABLE_NAME+'] TO ['+@name+']
REVOKE SELECT ON ['
+TABLE_SCHEMA+'].['+TABLE_NAME+'] TO ['+@name+']
REVOKE UPDATE ON ['
+TABLE_SCHEMA+'].['+TABLE_NAME+'] TO ['+@name+']
'    
ELSE
'
GRANT INSERT ON ['
+TABLE_SCHEMA+'].['+TABLE_NAME+'] TO ['+@name+']
-- GRANT DELETE ON ['
+TABLE_SCHEMA+'].['+TABLE_NAME+'] TO ['+@name+']
GRANT REFERENCES ON ['
+TABLE_SCHEMA+'].['+TABLE_NAME+'] TO ['+@name+']
GRANT SELECT ON ['
+TABLE_SCHEMA+'].['+TABLE_NAME+'] TO ['+@name+']
GRANT UPDATE ON ['
+TABLE_SCHEMA+'].['+TABLE_NAME+'] TO ['+@name+']
'    
END    
FROM
information_schema.tables WHERE TABLE_NAME NOT LIKE 'sys%'
SELECT @command=@command+ '
GRANT '
+CASE routine_type
        
WHEN 'procedure' THEN 'EXECUTE'
        
ELSE 'SELECT' END
    
+' ON ['+ROUTINE_SCHEMA+'].['+ROUTINE_NAME+'] TO ['+@name+']'
FROM information_Schema.routines
WHERE ROUTINE_NAME <> 'spDoAllPermissions'
EXECUTE (@Command)


/*The Test harness
  ----------------
Now the test database is constructed, we can try out the two
different security models

Firstly, open up a new window in SSMS or Query
Manager, but using the workbench login ID and 'mypassword' password


You can try out the following- paste it all into the new window.
Don't execute it in this window!

To execute this following code, you must be logged in as WorkBench
(password: mypassword)
don't execute it whilst logged in as DBO!
to change between the two secuity schemes, execute one of the two
blocks of code in the section 'Reassigning those 'Deny' roles'*/


USE SecurityWorkbench
-- can we use a view?
SELECT * FROM vcustomer

-- what about executing a stored procedure that returns the (censored)
-- data from the table
EXECUTE spCustomer

-- Can you use a stored procedure that contains dynamic SQL. If not,
-- then why not (The answer is in the SQL Server Security Cribsheet)
EXECUTE spCustomerWithDynamicSQL

-- Msg 229, Level 14, State 5, Line 1
--     SELECT permission denied on object 'Customer',
--     database 'SecurityWorkbench', owner 'dbo'.

-- can we access a table directly?
SELECT * FROM customer

-- or insert into a view?
INSERT INTO  vcustomer (firstname, surname,[user_ID])  
    
SELECT 'Akund','Swat','Who'

-- or indulge in wickedness?
master..xp_cmdshell 'Dir c:\'
DROP TABLE customer
DELETE FROM customer
SELECT * FROM information_schema.tables
EXECUTE sp_help
EXECUTE sp_who
KILL 52

-- can we access a table function?
SELECT * FROM dbo.uftCustomer() WHERE surname LIKE 'factor'

-- or use a view containing a table function?
SELECT * FROM  vCustomerViaFunction

-- and lastly, can we, without having any table access, check a
-- User_ID and password
DECLARE @success INT
EXECUTE sp
LogMeIn @user_id='jig',@password='flutersball',
    
@success=@success out --fixed 9/3/2007 
SELECT @success



This article has been viewed 7040 times.
Robyn Page and Phil Factor

Author profile: Robyn Page and Phil Factor

Robyn Page is a consultant with Enformatica and USP Networks. She is also a well known actress, being most famous for her role as Katie Williams, barmaid in the Television Series Family Affairs.

Phil Factor (real name withheld to protect the guilty), aka Database Mole, has 20 years of experience with database-intensive applications. Despite having once been shouted at by a furious Bill Gates at an exhibition in the early 1980s, he has remained resolutely anonymous throughout his career.

Search for other articles by Robyn Page and Phil Factor

Rate this article:   Avg rating: from a total of 24 votes.


Poor

OK

Good

Great

Must read
 
Have Your Say
Do you have an opinion on this article? Then add your comment below:
You must be logged in to post to this forum

Click here to log in.


Subject: SQL Injection is TOTALLY preventable!
Posted by: Anonymous (not signed in)
Posted on: Tuesday, March 06, 2007 at 1:33 PM
Message: The assertion that "It is impossible, purely from the database layer of an application alone, to prevent SQL injection attacks happening" is troublesome at best and extremely dangerous at worst. In fact, preventing SQL injection is well within the control of the database layer, and an injection attack can only occur in an improperly-designed/improperly-encapsulated database. It's also unfortunate that this article made no mention of how to properly fix SQL injection: proper use of parameterized queries renders injection attacks impossible, and is the only way to properly solve the problem. Escaping the string delimiter is NOT the correct approach, and can easily be defeated (search the Microsoft SQL Server newsgroups for posts by Steve Kass on this topic).

Subject: Re: SQL Injection is TOTALLY preventable
Posted by: Robyn Page (view profile)
Posted on: Tuesday, March 06, 2007 at 2:38 PM
Message: What I wrote was 'It is impossible, purely from the database layer of an application alone, to prevent SQL injection attacks happening'. I didn't say one could'nt prevent them succeeding. What I meant, of course, was that one should always be on one's guard, and one shouldn't rely on the application layer to insulate the database layer from attack. The article goes on to show how one can prevent it succeeding.
The use of parameterised queries, and the escaping of string delimiters is an application layer issue, and this article is about security at the Dattabase layer.

Subject: Re: SQL Injection is TOTALLY preventable
Posted by: WebMister (view profile)
Posted on: Tuesday, March 06, 2007 at 3:27 PM
Message: Mr Anonymous says that the proper use of parameterised queries renders injection attacks impossible. Quite so, but I suspect that an aven better approach is to validate the input properly, before passing it to the database. That also renders injection attacks impossible.
Robyn's point, as I read it, was that one cannot just rely on the application programmer to always get it right, but should take all necessary steps at the database level to ensure that, if an attack happens, it fails. I agree entirely.

Subject: Preventing an attack is the same as making sure it doesn't succeed
Posted by: Adam Machanic (view profile)
Posted on: Tuesday, March 06, 2007 at 11:07 PM
Message: It's difficult, if not impossible, to validate every injection vector properly. On the other hand, simply by using sp_executesql instead of EXEC for dynamic SQL (where necessary), you -completely eliminate- the possibility of SQL injection attacks (assuming that your app is not allowed to make ad hoc queries). No need for validation, escaping of delimiters, or any other string manipulation. You can pass in attempted injections all day and it will not matter, since the strings will be treated as arguments and not appended into a query string.

That is certainly a database layer security issue, and not within the realm of what the application should be expected to have properly taken care of. It is almost never a good idea, from a security standpoint, to implicitly trust that a higher layer is doing the right thing, and for the database that's doubly true.

Subject: Validation and Passwords
Posted by: Anonymous (not signed in)
Posted on: Tuesday, March 06, 2007 at 11:10 PM
Message: Nice article as always. It's particularly important to perform the additional validations if you are doing any kind of server-side dynamic SQL that depends on user input from the front end/application layer.

One thing that might have been worth mentioning (since this article is on security) is that passwords should not be stored as plain text in the database under any conditions. It's common practice to either encrypt passwords or hash them before storing them as VARBINARY in the tables.

Of course you might cover that in Part 2 (I just noticed the "Part 1" in the title) :)

Thanks

Subject: Adam are you sure?
Posted by: Anonymous (not signed in)
Posted on: Tuesday, March 06, 2007 at 11:17 PM
Message: "On the other hand, simply by using sp_executesql instead of EXEC for dynamic SQL (where necessary), you -completely eliminate- the possibility of SQL injection attacks (assuming that your app is not allowed to make ad hoc queries)."

Adam, does "assuming that your app is not allowed to make ad hoc queries" also cover the client passing object names to the server (database names, table names, column names, etc.) which might then be concatenated into a query server-side and then executed using sp_executesql?

-- Q&D example. You can't parameterize the table name...
CREATE PROCEDURE DoSomething (@table NVARCHAR(100))
AS
DECLARE @sql NVARCHAR(200)
SELECT @sql = N'SELECT * FROM ' + @table + ' WHERE X = Y'
EXECUTE sp_executesql @sql
GO

Subject: Well that begs another question...
Posted by: Adam Machanic (view profile)
Posted on: Tuesday, March 06, 2007 at 11:55 PM
Message: Which is, why are you passing a table name? Chances are if you're doing that you need to revisit your database design anyway. Why do you have multiple tables that can be used for the same purpose (indicates a lack of proper domain design), and why does the app tier have any knowledge of the table names (indicates a lack of proper encapsulation)?

If you really must pass object names, use the QUOTENAME function to delimit them.

Subject: By the way...
Posted by: Adam Machanic (view profile)
Posted on: Wednesday, March 07, 2007 at 12:04 AM
Message: ... SQL injection is generally considered to be a risk from strings passed by users... Are you really letting your users type in the table name they want to select from?

Subject: re Exec and executeSQL
Posted by: Phil Factor (view profile)
Posted on: Wednesday, March 07, 2007 at 3:00 AM
Message:

Interesting topic, Adam; thanks for the contribution.

What Robyn and I illustrate in this workbench is that you can't use ExecuteSQL or Exec to access base tables, if you have your permissions and roles properly tied down. Sorry if we didn't make that plain.
From her cribsheet I quote 'Sometimes, a developer will come up against the problem of 'ownership chains'. When a view or stored procedure is used, permissions are only checked for the contributing objects if there is a change of ownership somewhere along the chain. The most common time this happens is when 'Dynamic SQL' is executed by an Execute() or sp_executeSQL. And the user executing the procedure has no permission to access the objects involved. This is known as a Broken Ownership chain, because more than one user owns objects in a dependency chain.'

I have never come across any overriding requirement for execute() or sp_execute to access base tables in an application. There has always been a way around it in my experience. Perhaps someone else knows of a good case for allowing this? It certainly complicates security.


Subject: protecting the database
Posted by: Anonymous (not signed in)
Posted on: Wednesday, March 07, 2007 at 4:24 AM
Message: assuming protection is in the application or middle tier(s) will lead to sloppy database design. If you need a secure database then you start out designing it that way - I find security is often added afterwards instead of making security part of the initial design. Having been there and done that I'd never assume security is handled outside the database and I'd make the database secure ( as well ) . A secure database requires certain constraints, for example no dynamic/embedded/ad-hoc sql, no table permissions and so on , so it's a design criteria. There are lots of good white papers out there on sql injection SPI Dynamics produce some outstanding work have a read and see how easy it is and how many myths about protection exist.

Subject: Security boundaries
Posted by: Anonymous (not signed in)
Posted on: Wednesday, March 07, 2007 at 6:53 AM
Message: Along with everything mentioned above I make it a point to observe security boundaries. Specifically, if I know that the data arriving at the stored procedure has crossed a security boundary, Then I am going to implement additional security checking, e.g. making sure that what is CLAIMED to be passed in a parameter is in fact what is passed and looking for telltale signs of SQL Injection.

Is it overkill? Perhaps. But no one is going to think so if the data is compromised and some simple checks would have prevented it. What we are talking about is reasonable precautions and secure designs make sure that those reasonable precautions are being taken.

Does my method add to the overhead? Certainly, just like every Try...Catch block. It is not significant if implemented with some care. And if that overhead keeps your company from being the next one named on CNN for having 1 million credit card numbers lifted, then it was worth the price.

Subject: Broken ownership chains...
Posted by: Adam Machanic (view profile)
Posted on: Wednesday, March 07, 2007 at 9:29 AM
Message: Phil,

Broken ownership chains are certainly an issue in SQL Server 2000, but no longer in 2005, thanks to some of the new security context features (EXECUTE AS, and module signing). I haven't seen an article here on the topic here yet; perhaps I'll write one :)


Subject: Schema for SQL 2005
Posted by: Anonymous (not signed in)
Posted on: Wednesday, March 07, 2007 at 2:57 PM
Message: Excellent article. If a DBA is using SQL 2005, they should consider the use of schemas. I have successfully cleaned up my security for a database by putting all my stored procedures in a specific schema and then granting the database user (i.e. Workbench) execute rights on the schema. All of the concepts of this article still apply, just less grant statements are needed with a schema. I think the best part about schemas is when you add new stored procedures to it. The use of schemas eliminates the need to rerun the spDoAllPermissions after creating new stored procs.

Subject: results of an SQL Query directly into a CSV file
Posted by: Chaume Kaulven (view profile)
Posted on: Thursday, March 08, 2007 at 11:51 AM
Message: Hello miss robyn. How can I import sql query result directly into a CSV file?please gave me an idea.

Subject: Re: results of an SQL Query directly into a CSV file
Posted by: Robyn Page (view profile)
Posted on: Thursday, March 08, 2007 at 4:28 PM
Message: Sorry, Off-topic. Ask in the Forum SQL Server/SQL Server for beginners. The answer is OSQL or BCP, by the way.

Subject: minor misspelling
Posted by: papabear (view profile)
Posted on: Thursday, March 08, 2007 at 5:11 PM
Message: Quick note - it appears that the spLogMeIn is called as LogMeIn in your testing script. I tried to email tony.davis@simple-talk.com from the contact us page since I did not think that the forum was the right place to put this but I got a 550 (unknown user) back.
Also, you rock! Love the workbenches!

Subject: re: minor misspelling
Posted by: Robyn Page (view profile)
Posted on: Friday, March 09, 2007 at 1:55 AM
Message: Oops. Corrected. I blame Phil!

Subject: sorry
Posted by: Chaume Kaulven (view profile)
Posted on: Monday, March 12, 2007 at 9:21 AM
Message: Im sorry miss robin.. and thank you..

 









Phil Factor
Bunnikins!
 When an IT manager is selected as a victim of office politics of a large corporate, it is time for him to engage in... Read more...



 View the blog
SQL Toolbelt 2008: Predominantly an Engineering Task
 The conversion of the Red-Gate tools to be compatible with SQL Server 2008 might not seem, on first... Read more...

Audit Crosschecks
 In this short article, the second of a 2-part series, William suggests a solution, using SQL Data... Read more...

Discovering Security Uses for SQL Compare
 Much of the security of SQL Server is implemented as part of the database schema. This provides some... Read more...

XML Jumpstart Workbench
 In which Robyn and Phil decide that the best way of starting to learn XML is to jump in and take a ride... Read more...

RSS Newsfeed Workbench
 Robyn and Phil decide to build an RSS newsfeed in TSQL, using the power of SQL Server's XML.  Read more...

Beginning SQL Server 2005 Reporting Services Part 1
 Steve Joubert begins an in-depth tour of SQL Server 2005 Reporting Services with a step-by-step guide... Read more...

Ten Common Database Design Mistakes
 Database design and implementation is the cornerstone of any data centric project (read 99.9% of... Read more...

SQL Server Full Text Search Language Features
 SQL Full-text Search (SQL FTS) is an optional component of SQL Server 7 and later, which allows fast... Read more...

Beginning SQL Server 2005 Reporting Services Part 2
 Continuing his in-depth tour of SQL Server 2005 Reporting Services, Steve Joubert demonstrates the most... Read more...

Executing SSIS Packages
 Nigel Rivett demonstrates how to execute all SSIS packages in a given folder using either an SSIS... Read more...

Over 150,000 Microsoft professionals subscribe to the Simple-Talk technical journal. Join today, it's fast, simple, free and secure.

Join Simple Talk